/* * * * Copyright (c) 2016. David Sowerby * * * * 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 uk.q3c.krail.core.option; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import uk.q3c.krail.core.i18n.I18NKey; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import static com.google.common.base.Preconditions.checkNotNull; /** * Represents the elements which go together to make up a unique {@link Option} key within its context * <p> * Created by David Sowerby on 19/02/15. */ @Immutable public class OptionKey<T> { private final Class<? extends OptionContext> context; private final T defaultValue; @Nullable private final I18NKey descriptionKey; private final I18NKey key; private final List<String> qualifiers = new ArrayList<>(); public OptionKey(@Nonnull T defaultValue, @Nonnull OptionContext context, @Nonnull I18NKey nameKey, @Nonnull I18NKey descriptionKey, @Nullable String... qualifiers) { this(defaultValue, context.getClass(), nameKey, descriptionKey, qualifiers); } /** * @param context the class which uses the option * @param nameKey used as part of the overall key, and also as an I18N label for displaying options values to users * @param descriptionKey not used as part of the overall key, it provides an I18N description (usually a tooltip) for the option when displayed to users. * If the key * value (the I18N pattern) contains parameter slots, the qualifiers are assumed to be the parameters, in the order supplied. * @param qualifiers optional, this is usually dynamically generated qualifier(s) to make a complete unique identity where * the same option may be used several times within a context. If for example you have an array of * dynamically generated buttons, which you want the user to be able to individually choose the colours * of, you may have context=com.example.FancyButtonForm, key=BUTTON_COLOUR, qualifiers="2,3" * <p> * where "2,3" is the grid position of the button */ public OptionKey(@Nonnull T defaultValue, @Nonnull Class<? extends OptionContext> context, @Nonnull I18NKey nameKey, @Nullable I18NKey descriptionKey, @Nullable String... qualifiers) { this.defaultValue = defaultValue; checkNotNull(context); checkNotNull(nameKey); this.descriptionKey = descriptionKey; this.context = context; this.key = nameKey; addQualifiers(qualifiers); } /** * @param context the class which uses the option * @param nameKey used as part of the overall key, and also as an I18N label for displaying options values to users * @param descriptionKey not used as part of the overall key, it provides an I18N description (usually a tooltip) for the option when displayed to users. * If the key * value (the I18N pattern) contains parameter slots, the qualifiers are assumed to be the parameters, in the order supplied. * @param qualifiers optional, this is usually dynamically generated qualifier(s) to make a complete unique identity where * the same option may be used several times within a context. If for example you have an array of * dynamically generated buttons, which you want the user to be able to individually choose the colours * of, you may have context=com.example.FancyButtonForm, key=BUTTON_COLOUR, qualifiers="2,3" * <p> * where "2,3" is the grid position of the button */ public OptionKey(@Nonnull T defaultValue, @Nonnull Class<? extends OptionContext> context, @Nonnull I18NKey nameKey, @Nullable I18NKey descriptionKey, List<String> qualifiers) { this.defaultValue = defaultValue; checkNotNull(context); checkNotNull(nameKey); this.descriptionKey = descriptionKey; this.context = context; this.key = nameKey; this.qualifiers.addAll(qualifiers); } /** * Same as {@link #OptionKey} but with description key and qualifiers = null * * @param context the class which uses the option * @param nameKey used as part of the overall key, and also as an I18N label for displaying options values to users */ public OptionKey(@Nonnull T defaultValue, @Nonnull Class<? extends OptionContext> context, @Nonnull I18NKey nameKey) { this.defaultValue = defaultValue; checkNotNull(context); checkNotNull(nameKey); this.descriptionKey = null; this.context = context; this.key = nameKey; } /** * Same as {@link #OptionKey} but with description key and qualifiers = null * * @param context the class which uses the option * @param nameKey used as part of the overall key, and also as an I18N label for displaying options values to users */ public OptionKey(@Nonnull T defaultValue, @Nonnull OptionContext context, @Nonnull I18NKey nameKey) { this.defaultValue = defaultValue; checkNotNull(context); checkNotNull(nameKey); this.descriptionKey = null; this.context = context.getClass(); this.key = nameKey; } /** * Same as {@link #OptionKey} but with description key = null * * @param context the class which uses the option * @param nameKey used as part of the overall key, and also as an I18N label for displaying options values to users */ public OptionKey(@Nonnull T defaultValue, @Nonnull Class<? extends OptionContext> context, @Nonnull I18NKey nameKey, @Nullable String... qualifiers) { checkNotNull(context); checkNotNull(nameKey); checkNotNull(defaultValue); this.defaultValue = defaultValue; this.descriptionKey = null; this.context = context; this.key = nameKey; addQualifiers(qualifiers); } /** * Copy constructor which adds a qualifier to the {@code baseKey} * * @param baseKey the key on which to base the copy * @param qualifiers the qualifiers to append to the base key */ protected OptionKey(@Nonnull OptionKey<T> baseKey, String... qualifiers) { this(baseKey.getDefaultValue(), baseKey.getContext(), baseKey.getKey(), baseKey.getDescriptionKey()); this.qualifiers.addAll(baseKey.getQualifiers()); this.qualifiers.addAll(Arrays.asList(qualifiers)); } private void addQualifiers(String... qualifiers) { if (qualifiers != null) { this.qualifiers.addAll(Arrays.asList(qualifiers)); } } public T getDefaultValue() { return defaultValue; } @Nullable public I18NKey getDescriptionKey() { return descriptionKey; } @Nonnull public Class<? extends OptionContext> getContext() { return context; } @Nonnull public I18NKey getKey() { return key; } /** * returns a copy of this key with qualifiers added (functionally the same as using the copy constructor but looks neater when called) * * @param qualifiers the qualifiers to append to this key * @return a new instance with the qualifiers appended */ public OptionKey<T> qualifiedWith(String... qualifiers) { return new OptionKey(this, qualifiers); } public ImmutableList<String> getQualifiers() { return ImmutableList.copyOf(qualifiers); } /** * Returns a concatenation of the supplied parameters to form a composite String key * * @return a concatenation of the supplied parameters to form a composite String key */ @Nonnull public String compositeKey() { Joiner joiner = Joiner.on("-") .skipNulls(); Enum<?> e = (Enum<?>) key; ArrayList<String> params = new ArrayList<>(); params.add(context.getSimpleName()); params.add(e.name()); if (qualifiers != null) { for (String qualifier : qualifiers) { params.add(qualifier); } } return joiner.join(params); } @Override public boolean equals(Object other) { if (this == other) { return true; } if (!(other instanceof OptionKey)) { return false; } return this.compositeKey() .equals(((OptionKey) other).compositeKey()); } @Override public int hashCode() { return compositeKey().hashCode(); } }