Package au.net.causal.projo.prefs.transform

Source Code of au.net.causal.projo.prefs.transform.FontTransformer$Attributes

package au.net.causal.projo.prefs.transform;

import java.awt.Font;
import java.awt.font.TextAttribute;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import au.net.causal.projo.prefs.DataTypeSupport;
import au.net.causal.projo.prefs.PreferenceKeyMetadata;
import au.net.causal.projo.prefs.PreferencesException;
import au.net.causal.projo.prefs.TransformDataTypeSupportChain;
import au.net.causal.projo.prefs.TransformGetChain;
import au.net.causal.projo.prefs.TransformPutChain;
import au.net.causal.projo.prefs.TransformRemoveChain;
import au.net.causal.projo.prefs.TransformResult;

import com.google.common.collect.ImmutableList;
import com.google.common.reflect.TypeToken;

/**
* Provides ability to store and load {@link Font} values from stores.
* <p>
*
* When constructing the font transformer, a set of attributes are supplied which control which attributes of the font are stored and loaded.
* For ease-of-use, the {@link #createSimpleFontTransformer()} and {@link #createStandardFontTransformer()} methods can be used for common use-cases.
* <p>
*
* Font attributes are stored under multiple keys with appropriate data types (other transformers are used where necessary, such as when the store does not
* have native support for an attribute's data type).  The key name will be composed of the base name, a '.' character and the attribute name.  For example,
* with a base key name of 'menuFont', the keys 'menuFont.family' and 'menuFont.size' will be used to store the actual font information.
*
* @author prunge
*/
public class FontTransformer implements PreferenceTransformer
{
  private final Collection<? extends Attribute> attributes;
 
  /**
   * Creates a <code>FontTransformer</code> that stores and loads using the specified attributes.
   *
   * @param attributes the attributes to use.
   *
   * @throws NullPointerException if <code>attributes</code> is null.
   */
  public FontTransformer(Collection<? extends Attribute> attributes)
  {
    this.attributes = ImmutableList.copyOf(attributes);
  }
 
  /**
   * Creates a simple font transformer with common attributes.  These attributes support fonts or varying names and sizes and support bold/italic fonts.
   *
   * @return the created font transformer.
   */
  public static FontTransformer createSimpleFontTransformer()
  {
    List<Attribute> attributes = ImmutableList.of(
                    Attributes.FAMILY,
                    Attributes.SIZE,
                    Attributes.WEIGHT,
                    Attributes.POSTURE
                    );
   
    return(new FontTransformer(attributes));
  }
 
  /**
   * Creates a simple font transformer with typical attributes.  These attributes support fonts of varying names, sizes, bold/italic fonts, as well as
   * underline and strikethrough properties.
   *
   * @return the created font transformer.
   */
  public static FontTransformer createStandardFontTransformer()
  {
    List<Attribute> attributes = ImmutableList.of(
                    Attributes.FAMILY,
                    Attributes.SIZE,
                    Attributes.WEIGHT,
                    Attributes.POSTURE,
                    Attributes.UNDERLINE,
                    Attributes.STRIKETHROUGH
                    );
   
    return(new FontTransformer(attributes));
  }
 
  @Override
  public <T> TransformResult<T> applyGet(String key, PreferenceKeyMetadata<T> keyMetadata, TransformGetChain chain)
  throws PreferencesException
  {
    if (!isSupported(keyMetadata, chain))
      return(null);
   
    //Look up the component values
    Map<TextAttribute, Object> fontAttributeMap = new HashMap<>();
    for (Attribute attribute : attributes)
    {
      Object attributeValue = chain.getValue(key + attribute.getKeySuffix(), keyMetadata.withDataType(attribute.getStoredValueType()));
      if (attributeValue != null)
        fontAttributeMap.put(attribute.getTextAttribute(), attributeValue);
    }
   
    if (fontAttributeMap.isEmpty())
      return(new TransformResult<>(null));
   
    Font font = Font.getFont(fontAttributeMap);
   
    @SuppressWarnings("unchecked")
    TransformResult<T> result = new TransformResult<>((T)font);
   
    return(result);
  }

  @Override
  public <T> boolean applyPut(String key, T value, PreferenceKeyMetadata<T> keyMetadata, TransformPutChain chain)
  throws PreferencesException
  {
    if (!isSupported(keyMetadata, chain))
      return(false);
   
    //Store component values
    Font fValue = (Font)value;
   
    if (fValue == null)
    {
      for (Attribute attribute : attributes)
      {
        chain.putValue(key + attribute.getKeySuffix(), null, keyMetadata.withDataType(attribute.getStoredValueType()));
      }
    }
    else
    {
      for (Attribute attribute : attributes)
      {
        typeSafePutValue(chain, key + attribute.getKeySuffix(), attribute, attribute.getStoredValueType(), fValue, keyMetadata);
      }
    }
   
    return(true);
  }
 
  private <V> void typeSafePutValue(TransformPutChain chain, String key, Attribute attribute, Class<V> storedValueType, Font font, PreferenceKeyMetadata<?> baseKeyMetadata)
  throws PreferencesException
  {
    PreferenceKeyMetadata<V> componentKeyMetadata = baseKeyMetadata.withDataType(storedValueType);
    Object oValue = font.getAttributes().get(attribute.getTextAttribute());
   
    //Special handling - cases where font attribute might be Number but we need subclass type
    V value;
    if (oValue == null)
      value = null;
    else if (storedValueType.equals(attribute.getAllowedValueType()))
      value = storedValueType.cast(oValue);
    else if (attribute.getAllowedValueType().equals(Number.class))
    {
      Number nValue = (Number)oValue;
      if (storedValueType.equals(Float.class))
        value = storedValueType.cast(nValue.floatValue());
      else if (storedValueType.equals(Double.class))
        value = storedValueType.cast(nValue.doubleValue());
      else if (storedValueType.equals(Long.class))
        value = storedValueType.cast(nValue.longValue());
      else if (storedValueType.equals(Integer.class))
        value = storedValueType.cast(nValue.intValue());
      else if (storedValueType.equals(Short.class))
        value = storedValueType.cast(nValue.shortValue());
      else if (storedValueType.equals(Byte.class))
        value = storedValueType.cast(nValue.byteValue());
      else
        throw new PreferencesException("Cannot handle storedType=" + storedValueType.getCanonicalName() + ", allowedType=" + attribute.getAllowedValueType().getCanonicalName());
    }
    else
      throw new PreferencesException("Cannot handle storedType=" + storedValueType.getCanonicalName() + ", allowedType=" + attribute.getAllowedValueType().getCanonicalName());
   
    chain.putValue(key, value, componentKeyMetadata);
  }
 
  @Override
  public <T> boolean applyRemove(String key, PreferenceKeyMetadata<T> keyMetadata, TransformRemoveChain chain)
      throws PreferencesException
  {
    if (!isSupported(keyMetadata, chain))
      return(false);
   
    for (Attribute attribute : attributes)
    {
      PreferenceKeyMetadata<?> componentMetadata = keyMetadata.withDataType(attribute.getStoredValueType());
      chain.removeValue(key + attribute.getKeySuffix(), componentMetadata);     
    }
   
    return(true);
  }
 
  @Override
  public DataTypeSupport applyDataTypeSupport(PreferenceKeyMetadata<?> keyMetadata, TransformDataTypeSupportChain chain)
  throws PreferencesException
  {
    //Only for Font keys
    if (!keyMetadata.getDataType().equals(TypeToken.of(Font.class)))
      return(null);
   
    //If underlying store supports integers then add support, otherwise we can't touch it
    for (Attribute attribute : attributes)
    {
      if (!chain.isDataTypeSupported(keyMetadata.withDataType(attribute.getStoredValueType())))
        return(null);
    }
   
    //If we get here the transform will work
    return(DataTypeSupport.ADD_SUPPORT);
  }

  /**
   * Only use this transformer if:
   *
   * <ul>
   *   <li>Store does not support {@link Font} data type natively</li>
   *   <li>Store supports all the attribute data types of the font</li>
   *   <li>Key is a Font data type</li>
   * </ul>
   *
   * @throws PreferencesException if the underlying store fails to retrieve data type support information.
   */
  private boolean isSupported(PreferenceKeyMetadata<?> keyMetadata, TransformDataTypeSupportChain chain)
  throws PreferencesException
  {
    if (!TypeToken.of(Font.class).equals(keyMetadata.getDataType()))
      return(false);
    if (chain.isDataTypeSupportedNatively(keyMetadata.withDataType(Font.class)))
      return(false);
   
    for (Attribute attribute : attributes)
    {
      if (!chain.isDataTypeSupported(keyMetadata.withDataType(attribute.getStoredValueType())))
        return(false);
    }
   
    return(true);
  }
 
  /**
   * A single font attribute that wraps a {@link TextAttribute} and also defines the key suffix to apply to the base key when storing or loading in stores.
   *
   * @author prunge
   */
  public static class Attribute
  {
    private final String keySuffix;
    private final TextAttribute textAttribute;
    private final Class<?> storedValueType;
    private final Class<?> allowedValueType;
   
    /**
     * Creates an <code>Attribute</code>.
     * <p>
     *
     * Typically the <code>storedValueType</code> and <code>allowedValueType</code> will be the same.  The only exception will be when a text attribute
     * declares its data type as {@link Number}.  In this case, the <code>allowedValueType</code> will be Number and the <code>storedValueType</code>
     * will be some subclass of number that all number values will be converted to before storing.
     *
     * @param keySuffix the suffix to apply to the base key when storing or loading in a store.
     * @param textAttribute the text attribute to wrap.
     * @param storedValueType the value type to use for storing in the preference store.  Must be a sub-type of <code>allowedValueType</code>.  Use wrapper
     *       types instead of primitive types where applicable.
     * @param allowedValueType all value types that are allowed for the text attribute.
     *
     * @throws NullPointerException if any parameter is null.
     */
    public <V> Attribute(String keySuffix, TextAttribute textAttribute, Class<? extends V> storedValueType, Class<V> allowedValueType)
    {
      if (keySuffix == null)
        throw new NullPointerException("keySuffix == null");
      if (textAttribute == null)
        throw new NullPointerException("textAttribute == null");
      if (storedValueType == null)
        throw new NullPointerException("storedValueType == null");
      if (allowedValueType == null)
        throw new NullPointerException("allowedValueType == null");
     
      this.keySuffix = keySuffix;
      this.textAttribute = textAttribute;
      this.storedValueType = storedValueType;
      this.allowedValueType = allowedValueType;
    }

    /**
     * @return the suffix to apply to the base key when storing values of this attribute in preference stores.
     */
    public String getKeySuffix()
    {
      return(keySuffix);
    }

    /**
     * @return the text attribute of the font.
     */
    public TextAttribute getTextAttribute()
    {
      return(textAttribute);
    }

    /**
     * @return the value type for this attribute that is being stored in the preference store.
     */
    public Class<?> getStoredValueType()
    {
      return(storedValueType);
    }

    /**
     * @return the value type for this attribute that may be present for attribute values coming from {@link Font} objects.  Will be a supertype
     *       of {@linkplain #storedValueType stored value type}.
     */
    public Class<?> getAllowedValueType()
    {
      return(allowedValueType);
    }
  }
 
  /**
   * Common font attributes.
   *
   * @author prunge
   */
  public static final class Attributes
  {
    private static final String KEY_SEPARATOR = ".";
   
    /**
     * Font family.
     *
     * @see TextAttribute#FAMILY
     */
    public static final Attribute FAMILY = new Attribute(KEY_SEPARATOR + "family", TextAttribute.FAMILY, String.class, String.class);
   
    /**
     * Font size in points, stored as a floating point value.
     *
     * @see TextAttribute#SIZE
     */
    public static final Attribute SIZE = new Attribute(KEY_SEPARATOR + "size", TextAttribute.SIZE, Float.class, Number.class);
   
    /**
     * Font weight (boldness), stored as a floating point value.
     *
     * @see TextAttribute#WEIGHT
     */
    public static final Attribute WEIGHT = new Attribute(KEY_SEPARATOR + "weight", TextAttribute.WEIGHT, Float.class, Number.class);
   
    /**
     * Font posture (italic-ness), stored as a floating point value.
     *
     * @see TextAttribute#POSTURE
     */
    public static final Attribute POSTURE = new Attribute(KEY_SEPARATOR + "posture", TextAttribute.POSTURE, Float.class, Number.class);
   
    /**
     * Font underline style, stored as an integer.
     *
     * @see TextAttribute#UNDERLINE
     */
    public static final Attribute UNDERLINE = new Attribute(KEY_SEPARATOR + "underline", TextAttribute.UNDERLINE, Integer.class, Integer.class);
   
    /**
     * Font striketrhough option, stored as a boolean value.
     *
     * @see TextAttribute#STRIKETHROUGH
     */
    public static final Attribute STRIKETHROUGH = new Attribute(KEY_SEPARATOR + "strikethrough", TextAttribute.STRIKETHROUGH, Boolean.class, Boolean.class);
  }
}
TOP

Related Classes of au.net.causal.projo.prefs.transform.FontTransformer$Attributes

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.