All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.flybotix.hfr.codex.CodexMagic Maven / Gradle / Ivy

Go to download

A way for wifi robots and IoT devices to quickly send compressed data arrays across a network.

There is a newer version: 2019.1.11
Show newest version
package com.flybotix.hfr.codex;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import com.flybotix.hfr.codex.encode.AEncoder;
import com.flybotix.hfr.codex.encode.BitEncoder;
import com.flybotix.hfr.codex.encode.CompressedEncoder;
import com.flybotix.hfr.codex.encode.IEncoderProperties;
import com.flybotix.hfr.codex.encode.UncompressedEncoder;
import com.flybotix.hfr.util.lang.MethodCaller;

public final class CodexMagic {

  private Set> mProperties = new HashSet<>();
  private Map>, DefaultEncoders> mDefaultEncoders = new HashMap<>();
  
  /**
   * Creates a codex based upon the passed-in enumeration
   * @param pEnum An enumeration that implements the CodexOf interface
   * @return A codex
   * @param  The type backing the codex
   * @param  The enumeration backing the codex
   */
  public  & CodexOf> Codex thisEnum(Class pEnum) {
    return thisEnum(pEnum, -1);
  }
  
  /**
   * Creates a codex based upon the passed-in enumeration
   * @param pEnum An enumeration that implements the CodexOf interface
   * @param pKey Set a specific composite key for this codex
   * @return A codex
   * @param  The type backing the codex
   * @param  The enumeration backing the codex
   */
  public  & CodexOf> Codex thisEnum(Class pEnum, int pKey) {
    return thisEnum(pEnum, pKey, false);
  }
  
  /**
   * Creates a codex based upon the passed-in enumeration
   * @param pEnum An enumeration that implements the CodexOf interface
   * @param pKey Set a specific composite key for this codex
   * @param pIsThreadsafe - if true this will return a synchronized version of the codex (WIP)
   * @return A codex
   * @param  The type backing the codex
   * @param  The enumeration backing the codex
   */
  public  & CodexOf> Codex thisEnum(Class pEnum, int pKey, boolean pIsThreadsafe) {
    Class valueClass = getTypeOfCodex(pEnum);
    IEncoderProperties props = findPropertiesForClass(valueClass);
    if(props == null) {
      throw new IllegalArgumentException("Unable to find a properties implementation associated to " + valueClass + ". " +
        " If it is primitive, notify the developer.  If it is a custom type, make one yourself and register it.");
    }
//    AEncoder enc = new CompressedEncoder<>(pEnum, props);
//    Codex result = pIsThreadsafe ? new ThreadsafeCodex<>(enc) : new Codex(enc);
    Codex result = pIsThreadsafe ? new ThreadsafeCodex<>(pEnum) : new Codex(pEnum);
    result.meta().setCompositeKey(pKey);
    return result;
  }

  /**
   * Uses the codex type of the enumeration to generate properties for the enumeration.  Advanced usage only.
   * @param pEnum The codex enumeration to register.
   * @param  The type backing the codex
   * @param  The enumeration backing the codex
   */
  public  & CodexOf> void registerEnum(Class pEnum) {
    Class valueClass = getTypeOfCodex(pEnum);
    IEncoderProperties props = findPropertiesForClass(valueClass);
    registerEnum(pEnum, props);
  }

  /**
   * Internally-caches the properties for usage with the codex enumeration.  Helps to ensure
   * identical encoding behavior when using multiple instances of codexes for a single enumeration.
   * @param pEnum The codex enumeration.
   * @param pProperties Encoder Properties
   * @param  The type backing the codex
   * @param  The enumeration backing the codex
   */
  public  & CodexOf> void registerEnum(Class pEnum, IEncoderProperties pProperties) {
    if(pProperties == null) {
      throw new IllegalArgumentException("Cannot create encoders & codexes when the EncoderProperties parameter is null.");
    }
    
    DefaultEncoders def = new DefaultEncoders<>(pEnum, pProperties);
    mDefaultEncoders.put(pEnum, def);
  }

  /**
   * @param pEnum a Codex-based enumeration
   * @return an encoder for the given Enum class
   * @param  The type backing the codex
   * @param  The enumeration backing the codex
   */
  public  & CodexOf> AEncoder of(Class pEnum) {
    Boolean useCompression = MethodCaller.getBooleanFromEnum(pEnum, "usesCompression", true);
    return of(pEnum, useCompression);
  }
  
  /**
   * @param pEnum A Codex-based enumeration
   * @param pUseCompression Should the encoder use compression? If the data is sometimes sparse, set this to true.  Otherwise, false.
   * @return an encoder for the given Enum class
   * @param  The type backing the codex
   * @param  The enumeration backing the codex
   */
  public  & CodexOf> AEncoder of(Class pEnum, boolean pUseCompression) {
    AEncoder result = null;
    if(!mDefaultEncoders.containsKey(pEnum)) {
      registerEnum(pEnum);
    }
    @SuppressWarnings("unchecked")
    DefaultEncoders encs = (DefaultEncoders) mDefaultEncoders.get(pEnum);
    if(pUseCompression) {
      result = encs.compressed;
    } else {
      result = encs.uncompressed;
    }
    return result;
  }
  
  /**
   * Creates an encoder for a Boolean-based Codex Enumeration.  Boolean encoders use the BitSet class rather
   * than an array of Booleans, making the entire thing very efficient to store and transmit.
   * @param pEnum An enumeration that implements CodexOf<Boolean>
   * @return An encoder for Boolean codexes
   * @param  The enumeration backing the codex
   */
  public static  & CodexOf> AEncoder getBooleanEncoder(Class pEnum) {
    return new BitEncoder(pEnum);
  }
  
  /**
   * Advaned Usage.  Sets custom proprties for a particular enumeration.  Custom proprties can override default
   * values of a codex, alter the encode/decode algorithms, etc.  This also allows for Codexes to be used
   * with complex objects rather than just primitives.
   * @param pEnum A codex-based enumeration
   * @param pProperties Properties to set for the codex
   * @param  The type backing the codex
   * @param  The enumeration backing the codex
   */
  public  & CodexOf> void registerProperties(Class pEnum, IEncoderProperties pProperties) {
    mProperties.add(pProperties);
    mDefaultEncoders.put(pEnum, new DefaultEncoders<>(pEnum, pProperties));
  }
  

  /**
   * @param pEnum A codex-based enumeration
   * @return The registered properties based upon the Type of the enum
   * @param  The type backing the codex
   * @param  The enumeration backing the codex
   */
  public  & CodexOf> IEncoderProperties getPropertiesForEnum(Class pEnum) {
    return findPropertiesForClass(getTypeOfCodex(pEnum));
  }
  
  /**
   * Holder method for compressed & uncompressed encoders of the primitive encoders
   *
   * @param  Primitive Type
   * @param  Codex-based enumeration
   */
  class DefaultEncoders & CodexOf> {
    private final AEncoder uncompressed;
    private final AEncoder compressed;
    
    public DefaultEncoders(Class pEnum) {
      this(pEnum, getPropertiesForEnum(pEnum));
    }
    
    public DefaultEncoders(Class pEnum, IEncoderProperties props) {
      uncompressed = new UncompressedEncoder<>(pEnum, props);
      compressed = new CompressedEncoder<>(pEnum, props);
    }
  }
  
  /*
   * Lookup method for properties.  Returns null if they are not found.
   * @param pClass
   * @return
   */
  @SuppressWarnings("unchecked")
  private  IEncoderProperties findPropertiesForClass(Class pClass) {
    IEncoderProperties result = null;
    for(IEncoderProperties encmap : mProperties) {
      if(encmap.getCodexType().equals(pClass)) {
        result = (IEncoderProperties) encmap;
        break;
      }
    }
    return result;
  }
  
  @SuppressWarnings("unchecked")
  private static  & CodexOf> Class getTypeOfCodex(Class pEnum) {
//    Class> forcecast = (Class>)pEnum;
    Class> forcecast = Class.class.cast(pEnum);
    Type[] iface = forcecast.getGenericInterfaces();
    Class resultType = null;
    for(Type t : iface) {
      if(t.toString().contains(CodexOf.class.getSimpleName())) {
        resultType = (Class) ((ParameterizedType)t).getActualTypeArguments()[0];
        break;
      }
    }
    
    return resultType;
  }
  
  static CodexMagic inst() {
    return Holder.instance;
  }
  
  private CodexMagic() {
    mProperties.add(DOUBLE_ENCODER_PROPERTIES);
    mProperties.add(LONG_ENCODER_PROPERTIES);
    mProperties.add(INTEGER_ENCODER_PROPERTIES);
  }
  private static class Holder {
    private static CodexMagic instance = new CodexMagic();
  }
  
  /**************************************************
   * Properties
   **************************************************/
  private static final IEncoderProperties INTEGER_ENCODER_PROPERTIES = new IEncoderProperties() {
    public Class getCodexType() {
      return Integer.class;
    }
    
    public Integer getDefaultValue(boolean pIsCompressedAlgorithm) {
      if(pIsCompressedAlgorithm)return null;
      else return Integer.MIN_VALUE;
    }

    public Integer[] generateEmptyArray(int pSize, boolean pIsCompressedAlgorithm) {
      Integer[] result = new Integer[pSize];
      Arrays.fill(result, getDefaultValue(pIsCompressedAlgorithm));
      return result;
    }

    @Override
    public int sizeOfSingle() {
      return Integer.BYTES;
    }

    @Override
    public Integer decodeSingle(ByteBuffer pData) {
      return pData.getInt();
    }

    @Override
    public void encodeSingle(ByteBuffer pData, Integer pValue) {
      pData.putInt(pValue);
    }
  };
  
  private static final IEncoderProperties LONG_ENCODER_PROPERTIES = new IEncoderProperties() {
    public Class getCodexType() {
      return Long.class;
    }
    
    public Long getDefaultValue(boolean pIsCompressedAlgorithm) {
      if(pIsCompressedAlgorithm)return null;
      else return Long.MIN_VALUE;
    }

    public Long[] generateEmptyArray(int pSize, boolean pIsCompressedAlgorithm) {
      Long[] result = new Long[pSize];
      Arrays.fill(result, getDefaultValue(pIsCompressedAlgorithm));
      return result;
    }

    @Override
    public int sizeOfSingle() {
      return Long.BYTES;
    }

    @Override
    public Long decodeSingle(ByteBuffer pData) {
      return pData.getLong();
    }

    @Override
    public void encodeSingle(ByteBuffer pData, Long pValue) {
      pData.putLong(pValue);
    }
  };
  
  private static final IEncoderProperties DOUBLE_ENCODER_PROPERTIES = new IEncoderProperties() {
    public Class getCodexType() {
      return Double.class;
    }
    
    public Double getDefaultValue(boolean pIsCompressedAlgorithm) {
      if(pIsCompressedAlgorithm)return null;
      else return Double.NaN;
    }

    public Double[] generateEmptyArray(int pSize, boolean pIsCompressedAlgorithm) {
      Double[] result = new Double[pSize];
      Arrays.fill(result, getDefaultValue(pIsCompressedAlgorithm));
      return result;
    }

    @Override
    public int sizeOfSingle() {
      return Double.BYTES;
    }

    @Override
    public Double decodeSingle(ByteBuffer pData) {
      return pData.getDouble();
    }

    @Override
    public void encodeSingle(ByteBuffer pData, Double pValue) {
      pData.putDouble(pValue);
    }
  };
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy