blob: e448ab34931fe8a67d37f2c253f900a266774034 [file] [log] [blame]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using JCG = J2N.Collections.Generic;
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>
/// Helper class for loading SPI classes from classpath (META-INF files).
/// This is a light impl of <c>java.util.ServiceLoader</c> but is guaranteed to
/// be bug-free regarding classpath order and does not instantiate or initialize
/// the classes found.
/// <para/>
/// @lucene.internal
/// </summary>
public class SPIClassIterator<S> : IEnumerable<Type>
{
private static readonly JCG.HashSet<Type> types = LoadTypes();
private static JCG.HashSet<Type> LoadTypes() // LUCENENET: Avoid static constructors (see https://github.com/apache/lucenenet/pull/224#issuecomment-469284006)
{
var result = new JCG.HashSet<Type>();
var assembliesToExamine = Support.AssemblyUtils.GetReferencedAssemblies();
// LUCENENET NOTE: The following hack is not required because we are using abstract factories
// and pure DI to ensure the order of the codecs are always correct during testing.
//// LUCENENET HACK:
//// Tests such as TestImpersonation.cs expect that the assemblies
//// are probed in a certain order. NamedSPILoader, lines 68 - 75 adds
//// the first item it sees with that name. So if you have multiple
//// codecs, it may not add the right one, depending on the order of
//// the assemblies that were examined.
//// This results in many test failures if Types from Lucene.Net.Codecs
//// are examined and added to NamedSPILoader first before
//// Lucene.Net.TestFramework.
//var testFrameworkAssembly = assembliesToExamine.FirstOrDefault(x => string.Equals(x.GetName().Name, "Lucene.Net.TestFramework", StringComparison.Ordinal));
//if (testFrameworkAssembly != null)
//{
// //assembliesToExamine.Remove(testFrameworkAssembly);
// //assembliesToExamine.Insert(0, testFrameworkAssembly);
// assembliesToExamine = new Assembly[] { testFrameworkAssembly }.Concat(assembliesToExamine.Where(a => !testFrameworkAssembly.Equals(a)));
//}
foreach (var assembly in assembliesToExamine)
{
try
{
foreach (var type in assembly.GetTypes().Where(x => x.IsPublic))
{
try
{
if (!IsInvokableSubclassOf<S>(type))
{
continue;
}
// We are looking for types with a default ctor
// (which is used in NamedSPILoader) or has a single parameter
// of type IDictionary<string, string> (for AnalysisSPILoader)
var matchingCtors = type.GetConstructors().Where(ctor =>
{
var parameters = ctor.GetParameters();
switch (parameters.Length)
{
case 0: // default ctor
return false; // LUCENENET NOTE: Now that we have factored Codecs into Abstract Factories, we don't need default constructors here
case 1:
return typeof(IDictionary<string, string>).IsAssignableFrom(parameters[0].ParameterType);
default:
return false;
}
});
if (matchingCtors.Any())
{
result.Add(type);
}
}
catch
{
// swallow
}
}
}
catch
{
// swallow
}
}
return result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool IsInvokableSubclassOf<T>(Type type)
{
return typeof(T).IsAssignableFrom(type) && !type.IsAbstract && !type.IsInterface;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static SPIClassIterator<S> Get()
{
return new SPIClassIterator<S>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public IEnumerator<Type> GetEnumerator()
{
return types.GetEnumerator();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}