blob: a9d867cd5b788c20b9304110af839832e9ee5c69 [file] [log] [blame]
using J2N.Text;
using Lucene.Net.Analysis.TokenAttributes;
using System;
using System.Reflection;
using Attribute = Lucene.Net.Util.Attribute;
using AttributeSource = Lucene.Net.Util.AttributeSource;
using BytesRef = Lucene.Net.Util.BytesRef;
using IAttribute = Lucene.Net.Util.IAttribute;
namespace Lucene.Net.Analysis
{
/*
* 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 CharTermAttribute = Lucene.Net.Analysis.TokenAttributes.CharTermAttribute;
using IAttributeReflector = Lucene.Net.Util.IAttributeReflector;
/// <summary>
/// A <see cref="Token"/> is an occurrence of a term from the text of a field. It consists of
/// a term's text, the start and end offset of the term in the text of the field,
/// and a type string.
/// <para/>
/// The start and end offsets permit applications to re-associate a token with
/// its source text, e.g., to display highlighted query terms in a document
/// browser, or to show matching text fragments in a KWIC (KeyWord In Context)
/// display, etc.
/// <para/>
/// The type is a string, assigned by a lexical analyzer
/// (a.k.a. tokenizer), naming the lexical or syntactic class that the token
/// belongs to. For example an end of sentence marker token might be implemented
/// with type "eos". The default token type is "word".
/// <para/>
/// A Token can optionally have metadata (a.k.a. payload) in the form of a variable
/// length byte array. Use <see cref="Index.DocsAndPositionsEnum.GetPayload()"/> to retrieve the
/// payloads from the index.
///
/// <para/><para/>
///
/// <para/><b>NOTE:</b> As of 2.9, Token implements all <see cref="IAttribute"/> interfaces
/// that are part of core Lucene and can be found in the <see cref="TokenAttributes"/> namespace.
/// Even though it is not necessary to use <see cref="Token"/> anymore, with the new <see cref="TokenStream"/> API it can
/// be used as convenience class that implements all <see cref="IAttribute"/>s, which is especially useful
/// to easily switch from the old to the new <see cref="TokenStream"/> API.
///
/// <para/><para/>
///
/// <para><see cref="Tokenizer"/>s and <see cref="TokenFilter"/>s should try to re-use a <see cref="Token"/>
/// instance when possible for best performance, by
/// implementing the <see cref="TokenStream.IncrementToken()"/> API.
/// Failing that, to create a new <see cref="Token"/> you should first use
/// one of the constructors that starts with null text. To load
/// the token from a char[] use <see cref="ICharTermAttribute.CopyBuffer(char[], int, int)"/>.
/// To load from a <see cref="string"/> use <see cref="ICharTermAttribute.SetEmpty()"/> followed by
/// <see cref="ICharTermAttribute.Append(string)"/> or <see cref="ICharTermAttribute.Append(string, int, int)"/>.
/// Alternatively you can get the <see cref="Token"/>'s termBuffer by calling either <see cref="ICharTermAttribute.Buffer"/>,
/// if you know that your text is shorter than the capacity of the termBuffer
/// or <see cref="ICharTermAttribute.ResizeBuffer(int)"/>, if there is any possibility
/// that you may need to grow the buffer. Fill in the characters of your term into this
/// buffer, with <see cref="string.ToCharArray(int, int)"/> if loading from a string,
/// or with <see cref="System.Array.Copy(System.Array, int, System.Array, int, int)"/>,
/// and finally call <see cref="ICharTermAttribute.SetLength(int)"/> to
/// set the length of the term text. See <a target="_top"
/// href="https://issues.apache.org/jira/browse/LUCENE-969">LUCENE-969</a>
/// for details.</para>
/// <para>Typical Token reuse patterns:
/// <list type="bullet">
/// <item><description> Copying text from a string (type is reset to <see cref="TypeAttribute.DEFAULT_TYPE"/> if not specified):
/// <code>
/// return reusableToken.Reinit(string, startOffset, endOffset[, type]);
/// </code>
/// </description></item>
/// <item><description> Copying some text from a string (type is reset to <see cref="TypeAttribute.DEFAULT_TYPE"/> if not specified):
/// <code>
/// return reusableToken.Reinit(string, 0, string.Length, startOffset, endOffset[, type]);
/// </code>
/// </description></item>
/// <item><description> Copying text from char[] buffer (type is reset to <see cref="TypeAttribute.DEFAULT_TYPE"/> if not specified):
/// <code>
/// return reusableToken.Reinit(buffer, 0, buffer.Length, startOffset, endOffset[, type]);
/// </code>
/// </description></item>
/// <item><description> Copying some text from a char[] buffer (type is reset to <see cref="TypeAttribute.DEFAULT_TYPE"/> if not specified):
/// <code>
/// return reusableToken.Reinit(buffer, start, end - start, startOffset, endOffset[, type]);
/// </code>
/// </description></item>
/// <item><description> Copying from one one <see cref="Token"/> to another (type is reset to <see cref="TypeAttribute.DEFAULT_TYPE"/> if not specified):
/// <code>
/// return reusableToken.Reinit(source.Buffer, 0, source.Length, source.StartOffset, source.EndOffset[, source.Type]);
/// </code>
/// </description></item>
/// </list>
/// A few things to note:
/// <list type="bullet">
/// <item><description><see cref="Clear()"/> initializes all of the fields to default values. this was changed in contrast to Lucene 2.4, but should affect no one.</description></item>
/// <item><description>Because <see cref="TokenStream"/>s can be chained, one cannot assume that the <see cref="Token"/>'s current type is correct.</description></item>
/// <item><description>The startOffset and endOffset represent the start and offset in the source text, so be careful in adjusting them.</description></item>
/// <item><description>When caching a reusable token, clone it. When injecting a cached token into a stream that can be reset, clone it again.</description></item>
/// </list>
/// </para>
/// <para>
/// <b>Please note:</b> With Lucene 3.1, the <see cref="CharTermAttribute.ToString()"/> method had to be changed to match the
/// <see cref="ICharSequence"/> interface introduced by the interface <see cref="ICharTermAttribute"/>.
/// this method now only prints the term text, no additional information anymore.
/// </para>
/// </summary>
public class Token : CharTermAttribute, ITypeAttribute, IPositionIncrementAttribute, IFlagsAttribute, IOffsetAttribute, IPayloadAttribute, IPositionLengthAttribute
{
private int startOffset, endOffset;
private string type = TypeAttribute.DEFAULT_TYPE;
private int flags;
private BytesRef payload;
private int positionIncrement = 1;
private int positionLength = 1;
/// <summary>
/// Constructs a <see cref="Token"/> will null text. </summary>
public Token()
{
string s = "fooobar";
s.ToCharArray();
}
/// <summary>
/// Constructs a <see cref="Token"/> with null text and start &amp; end
/// offsets. </summary>
/// <param name="start"> start offset in the source text </param>
/// <param name="end"> end offset in the source text </param>
public Token(int start, int end)
{
CheckOffsets(start, end);
startOffset = start;
endOffset = end;
}
/// <summary>
/// Constructs a <see cref="Token"/> with null text and start &amp; end
/// offsets plus the <see cref="Token"/> type. </summary>
/// <param name="start"> start offset in the source text </param>
/// <param name="end"> end offset in the source text </param>
/// <param name="typ"> the lexical type of this <see cref="Token"/> </param>
public Token(int start, int end, string typ)
{
CheckOffsets(start, end);
startOffset = start;
endOffset = end;
type = typ;
}
/// <summary>
/// Constructs a <see cref="Token"/> with null text and start &amp; end
/// offsets plus flags. NOTE: flags is EXPERIMENTAL. </summary>
/// <param name="start"> start offset in the source text </param>
/// <param name="end"> end offset in the source text </param>
/// <param name="flags"> The bits to set for this token </param>
public Token(int start, int end, int flags)
{
CheckOffsets(start, end);
startOffset = start;
endOffset = end;
this.flags = flags;
}
/// <summary>
/// Constructs a <see cref="Token"/> with the given term text, and start
/// &amp; end offsets. The type defaults to "word."
/// <b>NOTE:</b> for better indexing speed you should
/// instead use the char[] termBuffer methods to set the
/// term text. </summary>
/// <param name="text"> term text </param>
/// <param name="start"> start offset in the source text </param>
/// <param name="end"> end offset in the source text </param>
public Token(string text, int start, int end)
{
CheckOffsets(start, end);
Append(text);
startOffset = start;
endOffset = end;
}
/// <summary>
/// Constructs a <see cref="Token"/> with the given text, start and end
/// offsets, &amp; type. <b>NOTE:</b> for better indexing
/// speed you should instead use the char[] termBuffer
/// methods to set the term text. </summary>
/// <param name="text"> term text </param>
/// <param name="start"> start offset in the source text </param>
/// <param name="end"> end offset in the source text </param>
/// <param name="typ"> token type </param>
public Token(string text, int start, int end, string typ)
{
CheckOffsets(start, end);
Append(text);
startOffset = start;
endOffset = end;
type = typ;
}
/// <summary>
/// Constructs a <see cref="Token"/> with the given text, start and end
/// offsets, &amp; type. <b>NOTE:</b> for better indexing
/// speed you should instead use the char[] termBuffer
/// methods to set the term text. </summary>
/// <param name="text"> term text </param>
/// <param name="start"> start offset in the source text </param>
/// <param name="end"> end offset in the source text </param>
/// <param name="flags"> token type bits </param>
public Token(string text, int start, int end, int flags)
{
CheckOffsets(start, end);
Append(text);
startOffset = start;
endOffset = end;
this.flags = flags;
}
/// <summary>
/// Constructs a <see cref="Token"/> with the given term buffer (offset
/// &amp; length), start and end offsets
/// </summary>
/// <param name="startTermBuffer"> buffer containing term text </param>
/// <param name="termBufferOffset"> the index in the buffer of the first character </param>
/// <param name="termBufferLength"> number of valid characters in the buffer </param>
/// <param name="start"> start offset in the source text </param>
/// <param name="end"> end offset in the source text </param>
public Token(char[] startTermBuffer, int termBufferOffset, int termBufferLength, int start, int end)
{
CheckOffsets(start, end);
CopyBuffer(startTermBuffer, termBufferOffset, termBufferLength);
startOffset = start;
endOffset = end;
}
/// <summary>
/// Gets or Sets the position increment (the distance from the prior term). The default value is one.
/// </summary>
/// <exception cref="ArgumentException"> if value is set to a negative value. </exception>
/// <seealso cref="IPositionIncrementAttribute"/>
public virtual int PositionIncrement
{
get => positionIncrement;
set
{
if (value < 0)
{
throw new ArgumentException("Increment must be zero or greater: " + value);
}
this.positionIncrement = value;
}
}
/// <summary>
/// Gets or Sets the position length of this <see cref="Token"/> (how many positions this token
/// spans).
/// <para/>
/// The default value is one.
/// </summary>
/// <exception cref="ArgumentException"> if value
/// is set to zero or negative. </exception>
/// <seealso cref="IPositionLengthAttribute"/>
public virtual int PositionLength
{
get => positionLength;
set => this.positionLength = value;
}
/// <summary>
/// Returns this <see cref="Token"/>'s starting offset, the position of the first character
/// corresponding to this token in the source text.
/// <para/>
/// Note that the difference between <see cref="EndOffset"/> and <see cref="StartOffset"/>
/// may not be equal to termText.Length, as the term text may have been altered by a
/// stemmer or some other filter.
/// </summary>
/// <seealso cref="SetOffset(int, int)"/>
/// <seealso cref="IOffsetAttribute"/>
public int StartOffset => startOffset;
/// <summary>
/// Returns this <see cref="Token"/>'s ending offset, one greater than the position of the
/// last character corresponding to this token in the source text. The length
/// of the token in the source text is (<code>EndOffset</code> - <see cref="StartOffset"/>).
/// </summary>
/// <seealso cref="SetOffset(int, int)"/>
/// <seealso cref="IOffsetAttribute"/>
public int EndOffset => endOffset;
/// <summary>
/// Set the starting and ending offset.
/// </summary>
/// <exception cref="ArgumentException"> If <paramref name="startOffset"/> or <paramref name="endOffset"/>
/// are negative, or if <paramref name="startOffset"/> is greater than
/// <paramref name="endOffset"/> </exception>
/// <seealso cref="StartOffset"/>
/// <seealso cref="EndOffset"/>
/// <seealso cref="IOffsetAttribute"/>
public virtual void SetOffset(int startOffset, int endOffset)
{
CheckOffsets(startOffset, endOffset);
this.startOffset = startOffset;
this.endOffset = endOffset;
}
/// <summary>Gets or Sets this <see cref="Token"/>'s lexical type. Defaults to "word". </summary>
public string Type
{
get => type;
set => this.type = value;
}
/// <summary>
/// Get the bitset for any bits that have been set.
/// <para/>
/// This is completely distinct from <see cref="ITypeAttribute.Type" />, although they do share similar purposes.
/// The flags can be used to encode information about the token for use by other <see cref="Lucene.Net.Analysis.TokenFilter" />s.
/// </summary>
/// <seealso cref="IFlagsAttribute"/>
public virtual int Flags
{
get => flags;
set => this.flags = value;
}
/// <summary>
/// Gets or Sets this <see cref="Token"/>'s payload.
/// </summary>
/// <seealso cref="IPayloadAttribute"/>
public virtual BytesRef Payload
{
get => this.payload;
set => this.payload = value;
}
/// <summary>
/// Resets the term text, payload, flags, and positionIncrement,
/// startOffset, endOffset and token type to default.
/// </summary>
public override void Clear()
{
base.Clear();
payload = null;
positionIncrement = 1;
flags = 0;
startOffset = endOffset = 0;
type = TokenAttributes.TypeAttribute.DEFAULT_TYPE;
}
public override object Clone()
{
var t = (Token)base.Clone();
// Do a deep clone
if (payload != null)
{
t.payload = (BytesRef)payload.Clone();
}
return t;
}
/// <summary>
/// Makes a clone, but replaces the term buffer &amp;
/// start/end offset in the process. This is more
/// efficient than doing a full clone (and then calling
/// <see cref="ICharTermAttribute.CopyBuffer"/>) because it saves a wasted copy of the old
/// termBuffer.
/// </summary>
public virtual Token Clone(char[] newTermBuffer, int newTermOffset, int newTermLength, int newStartOffset, int newEndOffset)
{
var t = new Token(newTermBuffer, newTermOffset, newTermLength, newStartOffset, newEndOffset)
{
positionIncrement = positionIncrement,
flags = flags,
type = type
};
if (payload != null)
{
t.payload = (BytesRef)payload.Clone();
}
return t;
}
public override bool Equals(object obj)
{
if (obj == this)
{
return true;
}
if (obj is Token)
{
var other = (Token)obj;
return (startOffset == other.startOffset &&
endOffset == other.endOffset &&
flags == other.flags &&
positionIncrement == other.positionIncrement &&
(type == null ? other.type == null : type.Equals(other.type, StringComparison.Ordinal)) &&
(payload == null ? other.payload == null : payload.Equals(other.payload)) &&
base.Equals(obj)
);
}
else
{
return false;
}
}
public override int GetHashCode()
{
int code = base.GetHashCode();
code = code * 31 + startOffset;
code = code * 31 + endOffset;
code = code * 31 + flags;
code = code * 31 + positionIncrement;
if (type != null)
{
code = code * 31 + type.GetHashCode();
}
if (payload != null)
{
code = code * 31 + payload.GetHashCode();
}
return code;
}
// like clear() but doesn't clear termBuffer/text
private void ClearNoTermBuffer()
{
payload = null;
positionIncrement = 1;
flags = 0;
startOffset = endOffset = 0;
type = TokenAttributes.TypeAttribute.DEFAULT_TYPE;
}
/// <summary>
/// Shorthand for calling <see cref="Clear"/>,
/// <see cref="ICharTermAttribute.CopyBuffer(char[], int, int)"/>,
/// <see cref="SetOffset"/>,
/// <see cref="Type"/> (set) </summary>
/// <returns> this <see cref="Token"/> instance </returns>
public virtual Token Reinit(char[] newTermBuffer, int newTermOffset, int newTermLength, int newStartOffset, int newEndOffset, string newType)
{
CheckOffsets(newStartOffset, newEndOffset);
ClearNoTermBuffer();
CopyBuffer(newTermBuffer, newTermOffset, newTermLength);
payload = null;
positionIncrement = 1;
startOffset = newStartOffset;
endOffset = newEndOffset;
type = newType;
return this;
}
/// <summary>
/// Shorthand for calling <see cref="Clear"/>,
/// <see cref="ICharTermAttribute.CopyBuffer(char[], int, int)"/>,
/// <see cref="SetOffset"/>,
/// <see cref="Type"/> (set) on <see cref="TypeAttribute.DEFAULT_TYPE"/> </summary>
/// <returns> this <see cref="Token"/> instance </returns>
public virtual Token Reinit(char[] newTermBuffer, int newTermOffset, int newTermLength, int newStartOffset, int newEndOffset)
{
CheckOffsets(newStartOffset, newEndOffset);
ClearNoTermBuffer();
CopyBuffer(newTermBuffer, newTermOffset, newTermLength);
startOffset = newStartOffset;
endOffset = newEndOffset;
type = TokenAttributes.TypeAttribute.DEFAULT_TYPE;
return this;
}
/// <summary>
/// Shorthand for calling <see cref="Clear"/>,
/// <see cref="ICharTermAttribute.Append(string)"/>,
/// <see cref="SetOffset"/>,
/// <see cref="Type"/> (set) </summary>
/// <returns> this <see cref="Token"/> instance </returns>
public virtual Token Reinit(string newTerm, int newStartOffset, int newEndOffset, string newType)
{
CheckOffsets(newStartOffset, newEndOffset);
Clear();
Append(newTerm);
startOffset = newStartOffset;
endOffset = newEndOffset;
type = newType;
return this;
}
/// <summary>
/// Shorthand for calling <see cref="Clear"/>,
/// <see cref="ICharTermAttribute.Append(string, int, int)"/>,
/// <see cref="SetOffset"/>,
/// <see cref="Type"/> (set) </summary>
/// <returns> this <see cref="Token"/> instance </returns>
public virtual Token Reinit(string newTerm, int newTermOffset, int newTermLength, int newStartOffset, int newEndOffset, string newType)
{
CheckOffsets(newStartOffset, newEndOffset);
Clear();
Append(newTerm, newTermOffset, newTermOffset + newTermLength);
startOffset = newStartOffset;
endOffset = newEndOffset;
type = newType;
return this;
}
/// <summary>
/// Shorthand for calling <see cref="Clear"/>,
/// <see cref="ICharTermAttribute.Append(string)"/>,
/// <see cref="SetOffset"/>,
/// <see cref="Type"/> (set) on <see cref="TypeAttribute.DEFAULT_TYPE"/> </summary>
/// <returns> this <see cref="Token"/> instance </returns>
public virtual Token Reinit(string newTerm, int newStartOffset, int newEndOffset)
{
CheckOffsets(newStartOffset, newEndOffset);
Clear();
Append(newTerm);
startOffset = newStartOffset;
endOffset = newEndOffset;
type = TokenAttributes.TypeAttribute.DEFAULT_TYPE;
return this;
}
/// <summary>
/// Shorthand for calling <see cref="Clear"/>,
/// <see cref="ICharTermAttribute.Append(string, int, int)"/>,
/// <see cref="SetOffset"/>,
/// <see cref="Type"/> (set) on <see cref="TypeAttribute.DEFAULT_TYPE"/> </summary>
/// <returns> this <see cref="Token"/> instance </returns>
public virtual Token Reinit(string newTerm, int newTermOffset, int newTermLength, int newStartOffset, int newEndOffset)
{
CheckOffsets(newStartOffset, newEndOffset);
Clear();
Append(newTerm, newTermOffset, newTermOffset + newTermLength);
startOffset = newStartOffset;
endOffset = newEndOffset;
type = TokenAttributes.TypeAttribute.DEFAULT_TYPE;
return this;
}
/// <summary>
/// Copy the prototype token's fields into this one. Note: Payloads are shared. </summary>
/// <param name="prototype"> source <see cref="Token"/> to copy fields from </param>
public virtual void Reinit(Token prototype)
{
CopyBuffer(prototype.Buffer, 0, prototype.Length);
positionIncrement = prototype.positionIncrement;
flags = prototype.flags;
startOffset = prototype.startOffset;
endOffset = prototype.endOffset;
type = prototype.type;
payload = prototype.payload;
}
/// <summary>
/// Copy the prototype token's fields into this one, with a different term. Note: Payloads are shared. </summary>
/// <param name="prototype"> existing <see cref="Token"/> </param>
/// <param name="newTerm"> new term text </param>
public virtual void Reinit(Token prototype, string newTerm)
{
SetEmpty().Append(newTerm);
positionIncrement = prototype.positionIncrement;
flags = prototype.flags;
startOffset = prototype.startOffset;
endOffset = prototype.endOffset;
type = prototype.type;
payload = prototype.payload;
}
/// <summary>
/// Copy the prototype token's fields into this one, with a different term. Note: Payloads are shared. </summary>
/// <param name="prototype"> existing <see cref="Token"/> </param>
/// <param name="newTermBuffer"> buffer containing new term text </param>
/// <param name="offset"> the index in the buffer of the first character </param>
/// <param name="length"> number of valid characters in the buffer </param>
public virtual void Reinit(Token prototype, char[] newTermBuffer, int offset, int length)
{
CopyBuffer(newTermBuffer, offset, length);
positionIncrement = prototype.positionIncrement;
flags = prototype.flags;
startOffset = prototype.startOffset;
endOffset = prototype.endOffset;
type = prototype.type;
payload = prototype.payload;
}
public override void CopyTo(IAttribute target)
{
var to = target as Token;
if (to != null)
{
to.Reinit(this);
// reinit shares the payload, so clone it:
if (payload != null)
{
to.payload = (BytesRef)payload.Clone();
}
}
else
{
base.CopyTo(target);
((IOffsetAttribute)target).SetOffset(startOffset, endOffset);
((IPositionIncrementAttribute)target).PositionIncrement = positionIncrement;
((IPayloadAttribute)target).Payload = (payload == null) ? null : (BytesRef)payload.Clone();
((IFlagsAttribute)target).Flags = flags;
((ITypeAttribute)target).Type = type;
}
}
public override void ReflectWith(IAttributeReflector reflector)
{
base.ReflectWith(reflector);
reflector.Reflect(typeof(IOffsetAttribute), "startOffset", startOffset);
reflector.Reflect(typeof(IOffsetAttribute), "endOffset", endOffset);
reflector.Reflect(typeof(IPositionIncrementAttribute), "positionIncrement", positionIncrement);
reflector.Reflect(typeof(IPayloadAttribute), "payload", payload);
reflector.Reflect(typeof(IFlagsAttribute), "flags", flags);
reflector.Reflect(typeof(ITypeAttribute), "type", type);
}
private void CheckOffsets(int startOffset, int endOffset)
{
if (startOffset < 0 || endOffset < startOffset)
{
throw new ArgumentException("startOffset must be non-negative, and endOffset must be >= startOffset, " + "startOffset=" + startOffset + ",endOffset=" + endOffset);
}
}
/// <summary>
/// Convenience factory that returns <see cref="Token"/> as implementation for the basic
/// attributes and return the default impl (with &quot;Impl&quot; appended) for all other
/// attributes.
/// @since 3.0
/// </summary>
public static readonly AttributeSource.AttributeFactory TOKEN_ATTRIBUTE_FACTORY = new TokenAttributeFactory(AttributeSource.AttributeFactory.DEFAULT_ATTRIBUTE_FACTORY);
/// <summary>
/// <b>Expert:</b> Creates a <see cref="TokenAttributeFactory"/> returning <see cref="Token"/> as instance for the basic attributes
/// and for all other attributes calls the given delegate factory.
/// @since 3.0
/// </summary>
public sealed class TokenAttributeFactory : AttributeSource.AttributeFactory
{
internal readonly AttributeSource.AttributeFactory @delegate;
/// <summary>
/// <b>Expert</b>: Creates an <see cref="AttributeSource.AttributeFactory"/> returning <see cref="Token"/> as instance for the basic attributes
/// and for all other attributes calls the given delegate factory.
/// </summary>
public TokenAttributeFactory(AttributeSource.AttributeFactory @delegate)
{
this.@delegate = @delegate;
}
public override Attribute CreateAttributeInstance<T>()
{
var attClass = typeof(T);
return attClass.IsAssignableFrom(typeof(Token)) ? new Token() : @delegate.CreateAttributeInstance<T>();
}
public override bool Equals(object other)
{
if (this == other)
{
return true;
}
var af = other as TokenAttributeFactory;
if (af != null)
{
return this.@delegate.Equals(af.@delegate);
}
return false;
}
public override int GetHashCode()
{
return @delegate.GetHashCode() ^ 0x0a45aa31;
}
}
}
}