-
Notifications
You must be signed in to change notification settings - Fork 624
/
AttributeImpl.cs
206 lines (188 loc) · 8.84 KB
/
AttributeImpl.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
namespace Lucene.Net.Util
{
/*
* 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.
*/
/// <summary> Base class for Attributes that can be added to a
/// <see cref="Lucene.Net.Util.AttributeSource" />.
/// <para/>
/// Attributes are used to add data in a dynamic, yet type-safe way to a source
/// of usually streamed objects, e. g. a <see cref="Lucene.Net.Analysis.TokenStream" />.
/// </summary>
public abstract class Attribute : IAttribute // LUCENENET specific: Not implementing ICloneable per Microsoft's recommendation
{
/// <summary> Clears the values in this <see cref="Attribute"/> and resets it to its
/// default value. If this implementation implements more than one <see cref="Attribute"/> interface
/// it clears all.
/// </summary>
public abstract void Clear();
/// <summary>
/// This is equivalent to the anonymous class in the Java version of ReflectAsString
/// </summary>
private class StringBuilderAttributeReflector : IAttributeReflector
{
private readonly StringBuilder buffer;
private readonly bool prependAttClass;
public StringBuilderAttributeReflector(StringBuilder buffer, bool prependAttClass)
{
this.buffer = buffer;
this.prependAttClass = prependAttClass;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reflect<T>(string key, object value)
where T : IAttribute
{
Reflect(typeof(T), key, value);
}
public void Reflect(Type type, string key, object value)
{
if (buffer.Length > 0)
{
buffer.Append(',');
}
if (prependAttClass)
{
buffer.Append(type.Name).Append('#');
}
buffer.Append(key).Append('=');
if (value is null)
buffer.Append("null");
else
buffer.Append(value);
}
}
/// <summary>
/// This method returns the current attribute values as a string in the following format
/// by calling the <see cref="ReflectWith(IAttributeReflector)"/> method:
/// <list type="bullet">
/// <item><term>if <paramref name="prependAttClass"/>=true:</term> <description> <c>"AttributeClass.Key=value,AttributeClass.Key=value"</c> </description></item>
/// <item><term>if <paramref name="prependAttClass"/>=false:</term> <description> <c>"key=value,key=value"</c> </description></item>
/// </list>
/// </summary>
/// <seealso cref="ReflectWith(IAttributeReflector)"/>
public string ReflectAsString(bool prependAttClass)
{
StringBuilder buffer = new StringBuilder();
ReflectWith(new StringBuilderAttributeReflector(buffer, prependAttClass));
return buffer.ToString();
}
/// <summary>
/// This method is for introspection of attributes, it should simply
/// add the key/values this attribute holds to the given <see cref="IAttributeReflector"/>.
///
/// <para/>The default implementation calls <see cref="IAttributeReflector.Reflect(Type, string, object)"/> for all
/// non-static fields from the implementing class, using the field name as key
/// and the field value as value. The <see cref="IAttribute"/> class is also determined by Reflection.
/// Please note that the default implementation can only handle single-Attribute
/// implementations.
///
/// <para/>Custom implementations look like this (e.g. for a combined attribute implementation):
/// <code>
/// public void ReflectWith(IAttributeReflector reflector)
/// {
/// reflector.Reflect(typeof(ICharTermAttribute), "term", GetTerm());
/// reflector.Reflect(typeof(IPositionIncrementAttribute), "positionIncrement", GetPositionIncrement());
/// }
/// </code>
///
/// <para/>If you implement this method, make sure that for each invocation, the same set of <see cref="IAttribute"/>
/// interfaces and keys are passed to <see cref="IAttributeReflector.Reflect(Type, string, object)"/> in the same order, but possibly
/// different values. So don't automatically exclude e.g. <c>null</c> properties!
/// </summary>
/// <seealso cref="ReflectAsString(bool)"/>
public virtual void ReflectWith(IAttributeReflector reflector) // LUCENENET NOTE: This method was abstract in Lucene
{
Type clazz = this.GetType();
LinkedList<WeakReference<Type>> interfaces = AttributeSource.GetAttributeInterfaces(clazz);
if (interfaces.Count != 1)
{
throw new NotSupportedException(clazz.Name + " implements more than one Attribute interface, the default ReflectWith() implementation cannot handle this.");
}
interfaces.First.Value.TryGetTarget(out Type interf);
//problem: the interfaces list has weak references that could have expired already
FieldInfo[] fields = clazz.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
try
{
for (int i = 0; i < fields.Length; i++)
{
FieldInfo f = fields[i];
if (f.IsStatic) continue;
reflector.Reflect(interf, f.Name, f.GetValue(this));
}
}
catch (MemberAccessException e)
{
throw new Exception(e.ToString(), e);
}
}
/// <summary> The default implementation of this method accesses all declared
/// fields of this object and prints the values in the following syntax:
///
/// <code>
/// public String ToString()
/// {
/// return "start=" + startOffset + ",end=" + endOffset;
/// }
/// </code>
///
/// This method may be overridden by subclasses.
/// </summary>
public override string ToString() // LUCENENET NOTE: This method didn't exist in Lucene
{
StringBuilder buffer = new StringBuilder();
Type clazz = this.GetType();
FieldInfo[] fields = clazz.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static);
for (int i = 0; i < fields.Length; i++)
{
FieldInfo f = fields[i];
if (f.IsStatic)
continue;
//f.setAccessible(true); // {{Aroush-2.9}} java.lang.reflect.AccessibleObject.setAccessible
object value = f.GetValue(this);
if (buffer.Length > 0)
{
buffer.Append(',');
}
if (value == null)
{
buffer.Append(f.Name + "=null");
}
else
{
buffer.Append(f.Name + "=" + value);
}
}
return buffer.ToString();
}
/// <summary> Copies the values from this <see cref="Attribute"/> into the passed-in
/// <paramref name="target"/> attribute. The <paramref name="target"/> implementation must support all the
/// <see cref="IAttribute"/>s this implementation supports.
/// </summary>
public abstract void CopyTo(IAttribute target);
/// <summary> Shallow clone. Subclasses must override this if they
/// need to clone any members deeply,
/// </summary>
public virtual object Clone()
{
return base.MemberwiseClone();
}
}
}