/******************************************************************************* * Copyright 2014 Analog Devices, Inc. * * Licensed 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 com.analog.lyric.options; import net.jcip.annotations.Immutable; import org.eclipse.jdt.annotation.Nullable; import com.analog.lyric.collect.ConstructorRegistry; /** * Key for options with literal Java class values with accessible no-argument constructor. * <p> * @since 0.07 * @author Christopher Barber */ @Immutable public abstract class ConstructorOptionKey<SuperClass> extends ClassOptionKey<SuperClass> { private static final long serialVersionUID = 1L; /*-------------- * Construction */ /** * Construct a class option key. * @param declaringClass is the class containing the static field declaration for this key. * @param name is the name of static field declaration for this key. * @param superClass is the superclass of all valid values. Should be same as the declared * <SuperClass> parameter. * @param defaultValue is the default value of the option. Used when option is not set. * @since 0.07 */ public ConstructorOptionKey(Class<?> declaringClass, String name, Class<SuperClass> superClass, Class<? extends SuperClass> defaultValue) { super(declaringClass, name, superClass, defaultValue); } /*-------------------- * IOptionKey methods */ /** * {@inheritDoc} * <p> * Returns simple name of class {@code value} if in {@linkplain #getRegistry() registry} * and otherwise returns the fully qualified class name. */ @Override public Object convertToExternal(Class<? extends SuperClass> value) { String name = value.getSimpleName(); if (getRegistry().getClassOrNull(name) != value) { name = value.getName(); } return name; } /** * {@inheritDoc} * <p> * If the {@code value} is a string, this will attempt to look the class up in this * key's {@linkplain #getRegistry() registry} and otherwise will attempt * to load the class using {@linkplain Class#forName(String, boolean, ClassLoader) Class.forName} * with context class loader. */ @Override public Class<? extends SuperClass> convertToValue(@Nullable Object value) { if (value instanceof String) { Class<? extends SuperClass> c = getRegistry().getClassOrNull((String)value); if (c != null) { return c; } } return super.convertToValue(value); } @Override public Class<? extends SuperClass> validate(Class<? extends SuperClass> value, @Nullable IOptionHolder optionHolder) { Class<? extends SuperClass> c = super.validate(value, optionHolder); // Verify that class has a no-argument constructor try { c.getConstructor(); } catch (Exception ex) { throw new OptionValidationException("Class '%s' does not have an accessible no argument constructor.", c); } return c; } /*--------------- * Local methods */ /** * The constructor registry associated with this key, if any. * <p> * @see #convertToValue(Object) * @since 0.07 */ public abstract ConstructorRegistry<SuperClass> getRegistry(); /** * Instantiates new instance of class that is the current value of this option. * <p> * Looks up value of class using {@link #getOrDefault} and instantiates * it using {@link Class#newInstance()} with no arguments. * <p> * @param holder is a non-null option holder. * @since 0.07 */ public SuperClass instantiate(IOptionHolder holder) { try { return getOrDefault(holder).newInstance(); } catch (InstantiationException | IllegalAccessException ex) { throw new RuntimeException(ex); } } /** * Returns instance of class that is the current value of this option, instantiating if * different from existing value. * <p> * This is similar to {@link #instantiate} but will return {@code prev} if it its * non-null and is already an instance of the class retrieved by {@link #getOrDefault}. * <p> * @param holder is a non-null option holder. * @param prev is a possibly-null existing instance. * @since 0.07 */ public SuperClass instantiateIfDifferent(IOptionHolder holder, @Nullable SuperClass prev) { Class<? extends SuperClass> c = getOrDefault(holder); if (prev != null && prev.getClass() == c) { return prev; } try { return c.newInstance(); } catch (InstantiationException | IllegalAccessException ex) { throw new RuntimeException(ex); } } }