blob: 36f08d0f26a3bd396a7b7d93752127c5feb42b4b [file] [log] [blame]
using Lucene.Net.Diagnostics;
using Lucene.Net.Support;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Text;
/*
* dk.brics.automaton
*
* Copyright (c) 2001-2009 Anders Moeller
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* this SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* this SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Lucene.Net.Util.Automaton
{
/// <summary>
/// <see cref="Automaton"/> state.
/// <para/>
/// @lucene.experimental
/// </summary>
public class State : IComparable<State>
{
internal bool accept;
[WritableArray]
[SuppressMessage("Microsoft.Performance", "CA1819", Justification = "Lucene's design requires some writable array properties")]
public Transition[] TransitionsArray => transitionsArray;
// LUCENENET NOTE: Setter removed because it is apparently not in use outside of this class
private Transition[] transitionsArray = Arrays.Empty<Transition>();
internal int numTransitions = 0;// LUCENENET NOTE: Made internal because we already have a public property for access
internal int number;
internal int id;
internal static int next_id;
/// <summary>
/// Constructs a new state. Initially, the new state is a reject state.
/// </summary>
public State()
{
//ResetTransitions(); // LUCENENET: Let class initializer set these
id = next_id++;
}
/// <summary>
/// Resets transition set.
/// </summary>
internal void ResetTransitions()
{
transitionsArray = Arrays.Empty<Transition>();
numTransitions = 0;
}
internal class TransitionsEnumerable : IEnumerable<Transition>
{
private readonly State outerInstance;
public TransitionsEnumerable(State outerInstance)
{
this.outerInstance = outerInstance;
}
public virtual IEnumerator<Transition> GetEnumerator()
{
return new TransitionsEnumerator(this);
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private struct TransitionsEnumerator : IEnumerator<Transition>
{
private readonly TransitionsEnumerable outerInstance;
private Transition current;
private int i;
private readonly int upTo;
public TransitionsEnumerator(TransitionsEnumerable outerInstance)
{
this.outerInstance = outerInstance;
upTo = this.outerInstance.outerInstance.numTransitions;
i = 0;
current = default;
}
public bool MoveNext()
{
if (i < upTo)
{
current = outerInstance.outerInstance.transitionsArray[i++];
return true;
}
return false;
}
public Transition Current => current;
object System.Collections.IEnumerator.Current => Current;
public void Reset()
{
throw new NotSupportedException();
}
public void Dispose()
{
}
}
}
/// <summary>
/// Returns the set of outgoing transitions. Subsequent changes are reflected
/// in the automaton.
/// </summary>
/// <returns> Transition set. </returns>
public virtual IEnumerable<Transition> GetTransitions()
{
return new TransitionsEnumerable(this);
}
public virtual int NumTransitions => numTransitions;
public virtual void SetTransitions(Transition[] transitions)
{
this.numTransitions = transitions.Length;
this.transitionsArray = transitions;
}
/// <summary>
/// Adds an outgoing transition.
/// </summary>
/// <param name="t"> Transition. </param>
public virtual void AddTransition(Transition t)
{
if (numTransitions == transitionsArray.Length)
{
// LUCENENET: Resize rather than copy
Array.Resize(ref transitionsArray, ArrayUtil.Oversize(1 + numTransitions, RamUsageEstimator.NUM_BYTES_OBJECT_REF));
}
transitionsArray[numTransitions++] = t;
}
/// <summary>
/// Sets acceptance for this state. If <c>true</c>, this state is an accept state.
/// </summary>
public virtual bool Accept
{
get => accept;
set => this.accept = value;
}
/// <summary>
/// Performs lookup in transitions, assuming determinism.
/// </summary>
/// <param name="c"> Codepoint to look up. </param>
/// <returns> Destination state, <c>null</c> if no matching outgoing transition. </returns>
/// <seealso cref="Step(int, ICollection{State})"/>
public virtual State Step(int c)
{
if (Debugging.AssertsEnabled) Debugging.Assert(c >= 0);
for (int i = 0; i < numTransitions; i++)
{
Transition t = transitionsArray[i];
if (t.min <= c && c <= t.max)
{
return t.to;
}
}
return null;
}
/// <summary>
/// Performs lookup in transitions, allowing nondeterminism.
/// </summary>
/// <param name="c"> Codepoint to look up. </param>
/// <param name="dest"> Collection where destination states are stored. </param>
/// <seealso cref="Step(int)"/>
public virtual void Step(int c, ICollection<State> dest)
{
for (int i = 0; i < numTransitions; i++)
{
Transition t = transitionsArray[i];
if (t.min <= c && c <= t.max)
{
dest.Add(t.to);
}
}
}
/// <summary>
/// Virtually adds an epsilon transition to the target
/// <paramref name="to"/> state. this is implemented by copying all
/// transitions from <paramref name="to"/> to this state, and if
/// <paramref name="to"/> is an accept state then set accept for this state.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal virtual void AddEpsilon(State to)
{
if (to.accept)
{
accept = true;
}
foreach (Transition t in to.GetTransitions())
{
AddTransition(t);
}
}
/// <summary>
/// Downsizes transitionArray to numTransitions. </summary>
public virtual void TrimTransitionsArray()
{
if (numTransitions < transitionsArray.Length)
{
Array.Resize(ref transitionsArray, numTransitions); // LUCENENET: Resize rather than copy
}
}
/// <summary>
/// Reduces this state. A state is "reduced" by combining overlapping
/// and adjacent edge intervals with same destination.
/// </summary>
public virtual void Reduce()
{
if (numTransitions <= 1)
{
return;
}
SortTransitions(Transition.COMPARE_BY_DEST_THEN_MIN_MAX);
State p = null;
int min = -1, max = -1;
int upto = 0;
for (int i = 0; i < numTransitions; i++)
{
Transition t = transitionsArray[i];
if (p == t.to)
{
if (t.min <= max + 1)
{
if (t.max > max)
{
max = t.max;
}
}
else
{
if (p != null)
{
transitionsArray[upto++] = new Transition(min, max, p);
}
min = t.min;
max = t.max;
}
}
else
{
if (p != null)
{
transitionsArray[upto++] = new Transition(min, max, p);
}
p = t.to;
min = t.min;
max = t.max;
}
}
if (p != null)
{
transitionsArray[upto++] = new Transition(min, max, p);
}
numTransitions = upto;
}
/// <summary>
/// Returns sorted list of outgoing transitions.
/// </summary>
/// <param name="comparer"> Comparer to sort with. </param>
/// <returns> Transition list. </returns>
/// <summary>
/// Sorts transitions array in-place. </summary>
public virtual void SortTransitions(IComparer<Transition> comparer)
{
// mergesort seems to perform better on already sorted arrays:
if (numTransitions > 1)
{
ArrayUtil.TimSort(transitionsArray, 0, numTransitions, comparer);
}
}
/// <summary>
/// Return this state's number.
/// <para/>
/// Expert: Will be useless unless <see cref="Automaton.GetNumberedStates()"/>
/// has been called first to number the states. </summary>
/// <returns> The number. </returns>
public virtual int Number => number;
/// <summary>
/// Returns string describing this state. Normally invoked via
/// <see cref="Automaton.ToString()"/>.
/// </summary>
public override string ToString()
{
StringBuilder b = new StringBuilder();
b.Append("state ").Append(number);
if (accept)
{
b.Append(" [accept]");
}
else
{
b.Append(" [reject]");
}
b.Append(":\n");
foreach (Transition t in GetTransitions())
{
b.Append(" ").Append(t.ToString()).Append("\n");
}
return b.ToString();
}
/// <summary>
/// Compares this object with the specified object for order. States are
/// ordered by the time of construction.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual int CompareTo(State s)
{
return s.id - id;
}
// LUCENENET NOTE: DO NOT IMPLEMENT Equals()!!!
// Although it doesn't match GetHashCode(), checking for
// reference equality is by design.
// Implementing Equals() causes difficult to diagnose
// IndexOutOfRangeExceptions when using FuzzyTermsEnum.
// See GH-296.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
return id;
}
}
}