/*
 * 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.util;


import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.UndeclaredThrowableException;

An AttributeFactory creates instances of AttributeImpls.
/** * An AttributeFactory creates instances of {@link AttributeImpl}s. */
public abstract class AttributeFactory {
Returns an AttributeImpl for the supplied Attribute interface class.
Throws:
  • UndeclaredThrowableException – A wrapper runtime exception thrown if the constructor of the attribute class throws a checked exception. Note that attributes should not throw or declare checked exceptions; this may be verified and fail early in the future.
/** * Returns an {@link AttributeImpl} for the supplied {@link Attribute} interface class. * * @throws UndeclaredThrowableException A wrapper runtime exception thrown if the * constructor of the attribute class throws a checked exception. * Note that attributes should not throw or declare * checked exceptions; this may be verified and fail early in the future. */
public abstract AttributeImpl createAttributeInstance(Class<? extends Attribute> attClass) throws UndeclaredThrowableException;
Returns a correctly typed MethodHandle for the no-arg ctor of the given class.
/** * Returns a correctly typed {@link MethodHandle} for the no-arg ctor of the given class. */
static final MethodHandle findAttributeImplCtor(Class<? extends AttributeImpl> clazz) { try { return lookup.findConstructor(clazz, NO_ARG_CTOR).asType(NO_ARG_RETURNING_ATTRIBUTEIMPL); } catch (NoSuchMethodException | IllegalAccessException e) { throw new IllegalArgumentException("Cannot lookup accessible no-arg constructor for: " + clazz.getName(), e); } } private static final MethodHandles.Lookup lookup = MethodHandles.publicLookup(); private static final MethodType NO_ARG_CTOR = MethodType.methodType(void.class); private static final MethodType NO_ARG_RETURNING_ATTRIBUTEIMPL = MethodType.methodType(AttributeImpl.class);
This is the default factory that creates AttributeImpls using the class name of the supplied Attribute interface class by appending Impl to it.
/** * This is the default factory that creates {@link AttributeImpl}s using the * class name of the supplied {@link Attribute} interface class by appending <code>Impl</code> to it. */
public static final AttributeFactory DEFAULT_ATTRIBUTE_FACTORY = new DefaultAttributeFactory(); private static final class DefaultAttributeFactory extends AttributeFactory { private final ClassValue<MethodHandle> constructors = new ClassValue<MethodHandle>() { @Override protected MethodHandle computeValue(Class<?> attClass) { return findAttributeImplCtor(findImplClass(attClass.asSubclass(Attribute.class))); } }; DefaultAttributeFactory() {} @Override public AttributeImpl createAttributeInstance(Class<? extends Attribute> attClass) { try { return (AttributeImpl) constructors.get(attClass).invokeExact(); } catch (Error | RuntimeException e) { throw e; } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } private Class<? extends AttributeImpl> findImplClass(Class<? extends Attribute> attClass) { try { return Class.forName(attClass.getName() + "Impl", true, attClass.getClassLoader()).asSubclass(AttributeImpl.class); } catch (ClassNotFoundException cnfe) { throw new IllegalArgumentException("Cannot find implementing class for: " + attClass.getName()); } } }
Expert: AttributeFactory returning an instance of the given clazz for the attributes it implements. For all other attributes it calls the given delegate factory as fallback. This class can be used to prefer a specific AttributeImpl which combines multiple attributes over separate classes.
@lucene.internal
/** <b>Expert</b>: AttributeFactory returning an instance of the given {@code clazz} for the * attributes it implements. For all other attributes it calls the given delegate factory * as fallback. This class can be used to prefer a specific {@code AttributeImpl} which * combines multiple attributes over separate classes. * @lucene.internal */
public abstract static class StaticImplementationAttributeFactory<A extends AttributeImpl> extends AttributeFactory { private final AttributeFactory delegate; private final Class<A> clazz;
Expert: Creates an AttributeFactory returning clazz as instance for the attributes it implements and for all other attributes calls the given delegate factory.
/** <b>Expert</b>: Creates an AttributeFactory returning {@code clazz} as instance for the * attributes it implements and for all other attributes calls the given delegate factory. */
public StaticImplementationAttributeFactory(AttributeFactory delegate, Class<A> clazz) { this.delegate = delegate; this.clazz = clazz; } @Override public final AttributeImpl createAttributeInstance(Class<? extends Attribute> attClass) { return attClass.isAssignableFrom(clazz) ? createInstance() : delegate.createAttributeInstance(attClass); }
Creates an instance of A.
/** Creates an instance of {@code A}. */
protected abstract A createInstance(); @Override public boolean equals(Object other) { if (this == other) return true; if (other == null || other.getClass() != this.getClass()) return false; @SuppressWarnings("rawtypes") final StaticImplementationAttributeFactory af = (StaticImplementationAttributeFactory) other; return this.delegate.equals(af.delegate) && this.clazz == af.clazz; } @Override public int hashCode() { return 31 * delegate.hashCode() + clazz.hashCode(); } }
Returns an AttributeFactory returning an instance of the given clazz for the attributes it implements. The given clazz must have a public no-arg constructor. For all other attributes it calls the given delegate factory as fallback. This method can be used to prefer a specific AttributeImpl which combines multiple attributes over separate classes.

Please save instances created by this method in a static final field, because on each call, this does reflection for creating a MethodHandle.

/** Returns an AttributeFactory returning an instance of the given {@code clazz} for the * attributes it implements. The given {@code clazz} must have a public no-arg constructor. * For all other attributes it calls the given delegate factory as fallback. * This method can be used to prefer a specific {@code AttributeImpl} which combines * multiple attributes over separate classes. * <p>Please save instances created by this method in a static final field, because * on each call, this does reflection for creating a {@link MethodHandle}. */
public static <A extends AttributeImpl> AttributeFactory getStaticImplementation(AttributeFactory delegate, Class<A> clazz) { final MethodHandle constr = findAttributeImplCtor(clazz); return new StaticImplementationAttributeFactory<A>(delegate, clazz) { @Override protected A createInstance() { try { return (A) constr.invokeExact(); } catch (Error | RuntimeException e) { throw e; } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } }; } }