LUCENE-8894: Add APIs to tokenizer/charfilter/tokenfilter factories to get their SPI names from concrete classes

This commit is contained in:
Tomoko Uchida 2019-06-30 12:19:48 +09:00
parent 42a1eb0403
commit 7ac77ce441
7 changed files with 100 additions and 18 deletions

View File

@ -138,6 +138,8 @@ Improvements
* LUCENE-8855: Add Accountable to some Query implementations (ab, Adrien Grand)
* LUCENE-8894: Add APIs to find SPI names for Tokenizer/CharFilter/TokenFilter factory classes. (Tomoko Uchida)
Optimizations
* LUCENE-8796: Use exponential search instead of binary search in

View File

@ -21,6 +21,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
@ -31,6 +33,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -335,4 +338,23 @@ public abstract class AbstractAnalysisFactory {
public void setExplicitLuceneMatchVersion(boolean isExplicitLuceneMatchVersion) {
this.isExplicitLuceneMatchVersion = isExplicitLuceneMatchVersion;
}
/**
* Looks up SPI name (static "NAME" field) with appropriate modifiers.
* Also it must be a String class and declared in the concrete class.
* @return the SPI name
* @throws NoSuchFieldException - if the "NAME" field is not defined.
* @throws IllegalAccessException - if the "NAME" field is inaccessible.
* @throws IllegalStateException - if the "NAME" field does not have appropriate modifiers or isn't a String field.
*/
static String lookupSPIName(Class<? extends AbstractAnalysisFactory> service) throws NoSuchFieldException, IllegalAccessException, IllegalStateException {
final Field field = service.getField("NAME");
int modifier = field.getModifiers();
if (Modifier.isStatic(modifier) && Modifier.isFinal(modifier) &&
field.getType().equals(String.class) &&
Objects.equals(field.getDeclaringClass(), service)) {
return ((String) field.get(null));
}
throw new IllegalStateException("No SPI name defined.");
}
}

View File

@ -17,9 +17,7 @@
package org.apache.lucene.analysis.util;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Locale;
@ -83,21 +81,13 @@ public final class AnalysisSPILoader<S extends AbstractAnalysisFactory> {
String originalName = null;
Throwable cause = null;
try {
// Lookup "NAME" field with appropriate modifiers.
// Also it must be a String class and declared in the service class.
final Field field = service.getField("NAME");
int modifier = field.getModifiers();
if (Modifier.isStatic(modifier) && Modifier.isFinal(modifier) &&
field.getType().equals(String.class) &&
Objects.equals(field.getDeclaringClass(), service)) {
originalName = ((String)field.get(null));
originalName = AbstractAnalysisFactory.lookupSPIName(service);
name = originalName.toLowerCase(Locale.ROOT);
if (!isValidName(originalName)) {
throw new ServiceConfigurationError("The name " + originalName + " for " + service.getName() +
" is invalid: Allowed characters are (English) alphabet, digits, and underscore. It should be started with an alphabet.");
}
}
} catch (NoSuchFieldException | IllegalAccessException e) {
} catch (NoSuchFieldException | IllegalAccessException | IllegalStateException e) {
cause = e;
}
if (name == null) {

View File

@ -49,6 +49,15 @@ public abstract class CharFilterFactory extends AbstractAnalysisFactory {
return loader.availableServices();
}
/** looks up a SPI name for the specified char filter factory */
public static String findSPIName(Class<? extends CharFilterFactory> serviceClass) {
try {
return lookupSPIName(serviceClass);
} catch (NoSuchFieldException | IllegalAccessException | IllegalStateException e) {
throw new IllegalStateException(e);
}
}
/**
* Reloads the factory list from the given {@link ClassLoader}.
* Changes to the factories are visible after the method ends, all

View File

@ -48,6 +48,15 @@ public abstract class TokenFilterFactory extends AbstractAnalysisFactory {
return loader.availableServices();
}
/** looks up a SPI name for the specified token filter factory */
public static String findSPIName(Class<? extends TokenFilterFactory> serviceClass) {
try {
return lookupSPIName(serviceClass);
} catch (NoSuchFieldException | IllegalAccessException | IllegalStateException e) {
throw new IllegalStateException(e);
}
}
/**
* Reloads the factory list from the given {@link ClassLoader}.
* Changes to the factories are visible after the method ends, all

View File

@ -50,6 +50,15 @@ public abstract class TokenizerFactory extends AbstractAnalysisFactory {
return loader.availableServices();
}
/** looks up a SPI name for the specified tokenizer factory */
public static String findSPIName(Class<? extends TokenizerFactory> serviceClass) {
try {
return lookupSPIName(serviceClass);
} catch (NoSuchFieldException | IllegalAccessException | IllegalStateException e) {
throw new IllegalStateException(e);
}
}
/**
* Reloads the factory list from the given {@link ClassLoader}.
* Changes to the factories are visible after the method ends, all

View File

@ -0,0 +1,41 @@
/*
* 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.
*/
package org.apache.lucene.analysis.util;
import org.apache.lucene.analysis.charfilter.HTMLStripCharFilterFactory;
import org.apache.lucene.analysis.core.LowerCaseFilterFactory;
import org.apache.lucene.analysis.core.WhitespaceTokenizerFactory;
import org.apache.lucene.util.LuceneTestCase;
public class TestAbstractAnalysisFactory extends LuceneTestCase {
public void testLookupTokenizerSPIName() throws NoSuchFieldException, IllegalAccessException {
assertEquals("whitespace", AbstractAnalysisFactory.lookupSPIName(WhitespaceTokenizerFactory.class));
assertEquals("whitespace", TokenizerFactory.findSPIName(WhitespaceTokenizerFactory.class));
}
public void testLookupCharFilterSPIName() throws NoSuchFieldException, IllegalAccessException {
assertEquals("htmlStrip", AbstractAnalysisFactory.lookupSPIName(HTMLStripCharFilterFactory.class));
assertEquals("htmlStrip", CharFilterFactory.findSPIName(HTMLStripCharFilterFactory.class));
}
public void testLookupTokenFilterSPIName() throws NoSuchFieldException, IllegalAccessException{
assertEquals("lowercase", AbstractAnalysisFactory.lookupSPIName(LowerCaseFilterFactory.class));
assertEquals("lowercase", TokenFilterFactory.findSPIName(LowerCaseFilterFactory.class));
}
}