// -----------------------------------------------------------------------
//
//
// 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.
//
//
// -----------------------------------------------------------------------
namespace Lucene.Net.Util
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Lucene.Net.Support;
///
/// This class represents a slice of a an existing char[].
/// The property should never be null, default to if necessary.
///
///
///
/// This class might be transformed into a immutable value type depending on its use
/// in the Lucene code base.
///
///
public class CharsRef : IComparable, ICharSequence, ICloneable, IEnumerable, IEquatable
{
private static readonly char[] emptyArray = new char[0];
private static readonly UTF8Comparer comparer = new UTF8Comparer();
///
/// Initializes a new instance of the class.
///
public CharsRef()
{
this.Chars = emptyArray;
}
///
/// Initializes a new instance of the class.
///
/// The capacity.
public CharsRef(int capacity)
{
if (capacity < 0)
throw new ArgumentException("capacity can not be less than 0", "capacity");
this.Chars = new char[capacity];
}
///
/// Initializes a new instance of the class.
///
/// The source.
/// The offset.
/// The length.
/// Thrown when is null.
///
/// Thrown when the length of is less than and
/// combined.
///
public CharsRef(char[] source, int offset = 0, int length = 0)
{
if (source == null)
throw new ArgumentNullException("source");
if (source.Length < (offset + length))
throw new ArgumentException(
"The length of source, source.Length, must be equal to or greater than the offset & length combined.");
if (length == 0)
length = source.Length;
this.Chars = source;
this.Offset = offset;
this.Length = length;
}
///
/// Initializes a new instance of the class.
///
/// The source.
/// Thrown when is null.
public CharsRef(string source)
{
if (source == null)
throw new ArgumentNullException("source");
this.Chars = source.ToCharArray();
this.Length = this.Chars.Length;
}
///
/// Initializes a new instance of the class.
///
/// The source.
/// Thrown when is null.
public CharsRef(CharsRef source)
{
if (source == null)
throw new ArgumentNullException("source");
this.Chars = emptyArray;
this.Copy(source);
}
///
/// Gets the UT F16 sorted as UT f8 comparer.
///
/// The UT F16 sorted as UT f8 comparer.
public static IComparer UTF16SortedAsUTF8Comparer
{
get { return comparer; }
}
///
/// Gets or sets the chars.
///
/// The chars.
public char[] Chars { get; protected set; }
///
/// Gets or sets the length.
///
/// The length.
public int Length { get; protected set; }
///
/// Gets or sets the offset.
///
/// The offset.
public int Offset { get; protected set; }
///
/// Implements the operator ==.
///
/// The left.
/// The right.
/// The result of the operator.
public static bool operator ==(CharsRef left, CharsRef right)
{
if (left == null)
return right == null;
return left.Equals(right);
}
///
/// Implements the operator !=.
///
/// The left.
/// The right.
/// The result of the operator.
public static bool operator !=(CharsRef left, CharsRef right)
{
if (left == null)
return right != null;
return !left.Equals(right);
}
///
/// Implements the operator >.
///
/// The left.
/// The right.
/// The result of the operator.
public static bool operator >(CharsRef left, CharsRef right)
{
return left.CompareTo(right) > 0;
}
///
/// Implements the operator <.
///
/// The left.
/// The right.
/// The result of the operator.
public static bool operator <(CharsRef left, CharsRef right)
{
return left.CompareTo(right) < 0;
}
///
/// Appends the specified value.
///
/// The value.
/// The offset.
/// The length.
public void Append(char[] value, int offset = 0, int length = 0)
{
if (length == 0)
length = value.Length;
this.Grow(this.Offset + length);
Array.Copy(value, offset, this.Chars, this.Offset, length);
this.Length = length;
}
///
/// Finds the at the specified index.
///
/// The index.
/// An instance of .
public char CharAt(int index)
{
return this.Chars[index];
}
///
/// Clones this instance.
///
/// an cloned instance of
public CharsRef Clone()
{
return new CharsRef(this);
}
///
/// Compares to.
///
/// The other.
/// An instance of .
public int CompareTo(CharsRef other)
{
if (this.Equals(other))
return 0;
char[] leftChars = this.Chars,
rightChars = other.Chars;
int leftOffset = this.Offset,
rightOffset = other.Offset,
end = leftOffset + Math.Min(this.Length, other.Length);
while (leftOffset < end)
{
int leftChar = leftChars[leftOffset++];
int rightChar = rightChars[rightOffset++];
if (leftChar > rightChar)
return 1;
if (leftChar < rightChar)
return -1;
}
return this.Length - other.Length;
}
///
/// Copies the specified and resets the current instance's to 0.
///
/// The source.
public void Copy(CharsRef source)
{
this.Chars = ArrayUtil.Grow(this.Chars, source.Length);
Array.Copy(source.Chars, source.Offset, this.Chars, 0, source.Length);
this.Length = source.Length;
this.Offset = 0;
}
///
/// Copies the char[] into this instance and sets this instance's to 0.
///
/// The source.
/// The offset.
/// The length.
public void Copy(char[] source, int offset = 0, int length = 0)
{
if (length == 0)
length = source.Length;
this.Offset = 0;
this.Append(source, offset, length);
}
///
/// Determines whether the specified is equal to this instance.
///
/// The right.
/// true if is equal to this instance, otherwise false.
public bool Equals(CharsRef right)
{
if (this.Length != right.Length)
return false;
int rightOffset = right.Offset,
end = this.Offset + this.Length;
char[] rightChars = right.Chars,
leftChars = this.Chars;
for (int leftOffset = this.Offset; leftOffset < end; leftOffset++, rightOffset++)
{
if (leftChars[leftOffset] != rightChars[rightOffset])
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)
{
CharsRef otherCharRef = obj as CharsRef;
if (otherCharRef != null)
return this.Equals(otherCharRef);
if (!obj.IsCharSequence())
return false;
ICharSequence sequence = null;
if (obj is string)
{
sequence = obj.ToString().ToCharSequence();
}
else if (obj is ICharSequence)
{
sequence = (ICharSequence)obj;
}
else
{
sequence = ((IEnumerable)obj).ToCharSequence();
}
if (this.Length != sequence.Length)
return false;
int end = this.Length,
i = this.Offset,
j = 0;
while (end-- != 0)
{
if (this.Chars[i++] != sequence.CharAt(j++))
return false;
}
return true;
}
///
/// Gets the enumerator.
///
///
/// An instance of of char.
///
public IEnumerator GetEnumerator()
{
// there might be a better way to do this.
return this.Chars.ToList().GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
///
/// 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()
{
int result = 0, end = this.Offset + this.Length;
for (int i = this.Offset; i < end; i++)
result = (31 * result) + this.Chars[i];
return result;
}
///
/// Grows the specified length.
///
/// The length.
public void Grow(int length)
{
if (this.Chars.Length < length)
this.Chars = ArrayUtil.Grow(this.Chars, length);
}
///
/// Gets the subset sequence of characters from the current sequence.
///
/// The start.
/// The end.
///
/// An instance of .
///
public ICharSequence SubSequence(int start, int end)
{
return new CharsRef(this.Chars, this.Offset + start, this.Offset + end - 1);
}
///
/// Returns a representation of this instance. This override
/// creates a new string passing in the char array, offset, and length.
///
///
/// A that represents this instance.
///
public override string ToString()
{
return new string(this.Chars, this.Offset, this.Length);
}
object ICloneable.Clone()
{
return this.Clone();
}
private sealed class UTF8Comparer : IComparer
{
///
/// Compares the specified left.
///
///
///
/// http://icu-project.org/docs/papers/utf16_code_point_order.html
///
///
/// A good deal of the code was pulled from the icu-project.
///
///
/// The left.
/// The right.
///
/// 0 if and are equal. Returns an int less than 0
/// if the is less than the , otherwise an int greater than 0.
///
public int Compare(CharsRef left, CharsRef right)
{
if (left == right)
return 0;
char[] leftChars = left.Chars,
rightChars = right.Chars;
int leftOffset = left.Offset,
rightOffset = right.Offset,
end = leftOffset + Math.Min(left.Length, right.Length);
Func normalize = (value) => {
if (value >= 0xe000)
value -= 0x800;
else
value += 0x2000;
return value;
};
while (leftOffset < end)
{
int leftChar = leftChars[leftOffset++];
int rightChar = rightChars[rightOffset++];
if (leftChar != rightChar)
{
//// http://icu-project.org/docs/papers/utf16_code_point_order.html
//// fix up each value if both values are inside of or above the surrogate range. then compare.
if (leftChar >= 0xd800 && rightChar >= 0xd800)
{
leftChar = normalize(leftChar);
rightChar = normalize(rightChar);
}
return leftChar - rightChar;
}
}
return left.Length - right.Length;
}
}
}
}