Example #1
0
        /// <summary>
        /// Resets the match object provided and performs the detection
        /// using that object rather than creating a new match.
        /// </summary>
        /// <param name="targetUserAgent">
        /// The user agent string to use as the target
        /// </param>
        /// <param name="match">
        /// A match object created by a previous match, or via the
        /// <see cref="CreateMatch"/> method.
        /// </param>
        /// <returns>
        /// The match object passed to the method updated with
        /// results for the target user agent
        /// </returns>
        public Match Match(string targetUserAgent, Match match)
        {
            MatchState state;

            if (_userAgentCache != null &&
                String.IsNullOrEmpty(targetUserAgent) == false)
            {
                // Increase the cache requests.
                Interlocked.Increment(ref _userAgentCache.Requests);

                if (_userAgentCache._itemsActive.TryGetValue(targetUserAgent, out state) == false)
                {
                    // The user agent has not been checked previously. Therefore perform
                    // the match and store the results in the cache.
                    MatchNoCache(targetUserAgent, match);

                    // Record the match state in the cache for next time.
                    state = new MatchState(match);
                    _userAgentCache._itemsActive[targetUserAgent] = state;

                    // Increase the cache misses.
                    Interlocked.Increment(ref _userAgentCache.Misses);
                }
                else
                {
                    // The state of a previous match exists so the match should
                    // be configured based on the results of the previous state.
                    match.SetState(state);
                }
                _userAgentCache.AddRecent(targetUserAgent, state);
            }
            else
            {
                // The cache does not exist so call the non caching method.
                MatchNoCache(targetUserAgent, match);
            }

            return(match);
        }
        /// <summary>
        /// Constructs an instance of <see cref="MatchResult"/> based on the
        /// source provided.
        /// </summary>
        /// <param name="source"></param>
        internal MatchResult(MatchState source)
        {
            _dataSet              = source._dataSet;
            _elapsed              = source.Elapsed;
            _method               = source.Method;
            _nodesEvaluated       = source.NodesEvaluated;
            _rootNodesEvaluated   = source.RootNodesEvaluated;
            _signaturesCompared   = source.SignaturesCompared;
            _signaturesRead       = source.SignaturesRead;
            _stringsRead          = source.StringsRead;
            _closestSignatures    = source.ClosestSignatures;
            _lowestScore          = source.LowestScore;
            _targetUserAgent      = source.TargetUserAgent;
            _targetUserAgentArray = source.TargetUserAgentArray;

            if (_dataSet is IndirectDataSet)
            {
                // The match result will only store the index or offset of the
                // related entity type in the source dataset to avoid creating
                // duplicate instances in cases where the data set is an indirect
                // one and a cache, or no cache is being used. This approach
                // ensures a consistent memory profile.
                _signatureIndex = source.Signature != null ?
                                  (int?)source.Signature.Index :
                                  null;
                _profileOffsets = source.Profiles.Select(i => i.Index).ToArray();
                _nodeOffsets    = source.Nodes.Select(i => i.Index).ToArray();
            }
            else
            {
                // The entire data set is being held in memory so a direct
                // reference to the related entity instance can be stored.
                _signature = source.Signature;
                _profiles  = (Profile[])source.Profiles.Clone();
                _nodes     = source.Nodes.ToArray();
            }
        }
        /// <summary>
        /// Resets the match object provided and performs the detection
        /// using that object rather than creating a new match.
        /// </summary>
        /// <param name="targetUserAgent">
        /// The User-Agent string to use as the target
        /// </param>
        /// <param name="state">
        /// A match object created by a previous match, or via the
        /// <see cref="CreateMatch"/> method.
        /// </param>
        /// <returns>
        /// The match object passed to the method updated with
        /// results for the target User-Agent
        /// </returns>
        private MatchResult Match(string targetUserAgent, MatchState state)
        {
            MatchResult result;

            if (targetUserAgent == null)
            {
                // Handle null User-Agents as empty strings.
                targetUserAgent = String.Empty;
            }

            if (_userAgentCache != null)
            {
                // Fetch the item using the cache.
                result = _userAgentCache[targetUserAgent, state];
            }
            else
            {
                // The cache does not exist so call the non caching method.
                MatchNoCache(targetUserAgent, state);
                result = new MatchResult(state);
            }

            return(result);
        }
 /// <summary>
 /// Contructs a new detection match ready to used to identify
 /// profiles from User-Agents.
 /// </summary>
 /// <param name="provider"></param>
 internal Match(Provider provider)
 {
     Provider = provider;
     State    = new MatchState(this);
     Result   = State;
 }
 /// <summary>
 /// The detection failed and a default match needs to be returned.
 /// </summary>
 /// <param name="state">Information about the match state</param>
 internal static void MatchDefault(MatchState state)
 {
     state.Method = MatchMethods.None;
     state.ExplicitProfiles.Clear();
     state.ExplicitProfiles.AddRange(state.DataSet.Components.Select(i => i.DefaultProfile));
 }
        /// <summary>
        /// Entry point to the detection process. Provided with a <see cref="Match"/>
        /// configured with the information about the request.
        /// </summary>
        /// <remarks>
        /// The dataSet may be used by other threads in parallel and is not assumed to be used
        /// by only one detection process at a time.
        /// </remarks>
        /// <param name="state">The match state object to be updated.</param>
        /// <remarks>
        /// The memory implementation of the data set will always perform fastest but does
        /// consume more memory.
        /// </remarks>
        internal static void Match(MatchState state)
        {
            if (state.DataSet.Disposed)
            {
                throw new InvalidOperationException(
                          "Data Set has been disposed and can't be used for match");
            }

            // If the User-Agent is too short then don't try to match and
            // return defaults.
            if (state.TargetUserAgentArray.Length == 0 ||
                state.TargetUserAgentArray.Length < state.DataSet.MinUserAgentLength)
            {
                // Set the default values.
                MatchDefault(state);
            }
            else
            {
                // Starting at the far right evaluate the nodes in the data
                // set recording matched nodes. Continue until all character
                // positions have been checked.
                Evaluate(state);

                // Can a precise match be found based on the nodes?
                var signatureIndex = GetExactSignatureIndex(state);

                if (signatureIndex >= 0)
                {
                    // Yes a precise match was found.
                    state.Signature   = state.DataSet.Signatures[signatureIndex];
                    state.Method      = MatchMethods.Exact;
                    state.LowestScore = 0;
                }
                else
                {
                    // No. So find any other nodes that match if numeric differences
                    // are considered.
                    EvaluateNumeric(state);

                    // Can a precise match be found based on the nodes?
                    signatureIndex = GetExactSignatureIndex(state);

                    if (signatureIndex >= 0)
                    {
                        // Yes a precise match was found.
                        state.Signature = state.DataSet.Signatures[signatureIndex];
                        state.Method    = MatchMethods.Numeric;
                    }
                    else if (state.Nodes.Count > 0)
                    {
                        // Look for the closest signatures to the nodes found.
                        var closestSignatures = GetClosestSignatures(state);

#if DEBUG
                        // Validate the list is in ascending order of ranked signature index.
                        var enumerator = closestSignatures.GetEnumerator();
                        var lastIndex  = -1;
                        while (enumerator.MoveNext())
                        {
                            Debug.Assert(lastIndex < enumerator.Current);
                            lastIndex = enumerator.Current;
                        }
#endif

                        // Try finding a signature with identical nodes just
                        // not in exactly the same place.
                        _nearest.EvaluateSignatures(state, closestSignatures);

                        if (state.Signature != null)
                        {
                            // All the sub strings matched, just in different
                            // character positions.
                            state.Method = MatchMethods.Nearest;
                        }
                        else
                        {
                            // Find the closest signatures and compare them
                            // to the target looking at the smallest character
                            // difference.
                            _closest.EvaluateSignatures(state, closestSignatures);
                            state.Method = MatchMethods.Closest;
                        }
                    }
                }

                // If there still isn't a signature then set the default.
                if (state.Signature == null)
                {
                    MatchDefault(state);
                }
            }
        }
 /// <summary>
 /// Gets the score for the specific node of the signature.
 /// </summary>
 /// <param name="state"></param>
 /// <param name="node"></param>
 /// <returns></returns>
 protected abstract int GetScore(MatchState state, Node node);