| /* |
| * 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 {@link AttributeImpl}s. |
| */ |
| public abstract class AttributeFactory { |
| |
| /** |
| * 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 {@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 {@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()); |
| } |
| } |
| } |
| |
| /** <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; |
| |
| /** <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 {@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 {@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); |
| } |
| } |
| }; |
| } |
| } |