//
// CachingCoordinateSystem.java
//

/*
VisAD system for interactive analysis and visualization of numerical
data.  Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom
Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and
Tommy Jasmin.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA
*/

package visad.data;

import java.util.Arrays;
import java.util.Enumeration;
import java.util.Hashtable;

import visad.util.Util;

/**
 * This class is used by the CachingCoordinateSystem to do the actual caching mapping one array to another one
 * @version $Revision: 1.5 $ $Date: 2010-01-05 21:02:43 $
 */
public class ArrayCache {

  /** Do we cache */
  private boolean enabled =
    Boolean.parseBoolean(System.getProperty("visad.data.arraycache.enabled",
                                            "true"));

  /** lower size threshold */
  private int lowerThreshold =
    Integer.parseInt(System.getProperty("visad.data.arraycache.lowerthreshold",
                                        "1000"));


  /** upper size threshold */
  private int upperThreshold =
    Integer.parseInt(System.getProperty("visad.data.arraycache.upperthreshold",
                                        "1000000"));


  private boolean useDataCacheManager = 
      Boolean.parseBoolean(System.getProperty("visad.data.arraycache.usedatacachemanager",
                                              "false"));



  private Hashtable  misses = new Hashtable();





  /**
   *  ctor
   */
  public ArrayCache() {}


  /**
   * ctor
   *
   * @param enabled  If false then never cache
   */
  public ArrayCache(boolean enabled) {
    this.enabled = enabled;
  }

   private String getKey(String key, int size) {
       return key +"_" + size;
   }

  /**
   * Get the converted value for the specified key and input pairs
   *
   * @param key The key (e.g., "toReference", "fromReference")
   * @param input  The input
   *
   * @return value for supplied key
   */
   
  public FloatResult get(String key, float[][] input) {
    if (!shouldHandle(input)) {
        return new FloatResult(false);
    }
    return getInner(key, input);
  }


   private synchronized FloatResult getInner(String key, float[][] input) {
    key = getKey(key, input[0].length);
    float[][][] pair = getFloatValue(key);
    if (pair == null) {
      return handleCacheMiss(key, input);
    }

    float[][] lastInput = pair[0];
    float[][] lastOutput = pair[1];
    if (lastInput.length != input.length) return null;
    for (int i = 0; i < input.length; i++) {
      if (!Arrays.equals(input[i], lastInput[i])) {
          return handleCacheMiss(key, input);
      }
    }
    misses.remove(key);
    //?? should we clone the output
    return new FloatResult(Util.clone(lastOutput));
  }





  /**
   * Get the converted value for the specified key and input pairs
   *
   * @param key The key (e.g., "toReference", "fromReference")
   * @param input  The input
   *
   * @return value for the supplied key
   */
   
  public DoubleResult get(String key, double[][] input) {
      if (!shouldHandle(input)) {
         return new DoubleResult(false);
    }
      return getInner(key, input);
  }



 private synchronized  DoubleResult getInner(String key, double[][] input) {

    key = getKey(key, input[0].length);
    double[][][] pair = getDoubleValue(key);
    if (pair == null) {
        return handleCacheMiss(key, input);
    }        

    double[][] lastInput = pair[0];
    double[][] lastOutput = pair[1];
    if (lastInput.length != input.length) return null;
    for (int i = 0; i < input.length; i++) {
       if (!Arrays.equals(input[i], lastInput[i])) {
         return handleCacheMiss(key, input);
      }
    }
    misses.remove(key);
    return new DoubleResult(Util.clone(lastOutput));
  }



  private DoubleResult handleCacheMiss(String key, double[][]input) {
        Integer numMisses = misses.get(key);
        if(numMisses==null) {
            misses.put(key, numMisses = new Integer(1));
        } else {
            misses.put(key, new Integer(numMisses.intValue()+1));
        }
        if(numMisses.intValue()>3) {
            removeValue(key);
        }
        return new DoubleResult(numMisses.intValue()<=1);
  }


  private FloatResult handleCacheMiss(String key, float[][]input) {
        Integer numMisses = misses.get(key);
        if(numMisses==null) {
            misses.put(key, numMisses = new Integer(1));
        } else {
            misses.put(key, new Integer(numMisses.intValue()+1));
        }
        if(numMisses.intValue()>3) {
            removeValue(key);
        }
        return new FloatResult(numMisses.intValue()<=1);
  }





    private boolean shouldHandle(double[][]input) {
        if(input == null) return false;
        if(input.length==0) return false;
        if(input[0]==null) return false;
        if(!enabled) return false;
        if (input[0].length <= lowerThreshold) return false;
        if (input[0].length > upperThreshold) return false;
        return true;
    }



    private boolean shouldHandle(float[][]input) {
        if(input == null) return false;
        if(input.length==0) return false;
        if(input[0]==null) return false;
        if(!enabled) return false;
        if (input[0].length <= lowerThreshold) return false;
        if (input[0].length > upperThreshold) return false;
        return true;
    }



  /**
   * Put the converted value for the specified key and input pairs
   *
   * @param key The key
   * @param input  The input array
   * @param results  The array to store
   */
  public void put(String key, double[][] input, DoubleResult results) {
    if(!shouldHandle(input)) return;
    putInner(key, input, results);
  }


  private synchronized void putInner(String key, double[][] input, DoubleResult results) {
    if(!results.shouldCache || results.values==null) return;
    key = getKey(key, input[0].length);
    storeValue(key,
             new double[][][] {
                 (double[][])Util.clone(input), (double[][])Util.clone(results.values)
    });

  }




  /**
   * Put the converted value for the specified key and input pairs
   *
   * @param key The key
   * @param input  The input array
   * @param results  The array to store
   */
  public  void put(String key, float[][] input, FloatResult results) {
    if(!shouldHandle(input)) return;
    putInner(key, input, results);
  }

  private synchronized void putInner(String key, float[][] input, FloatResult results) {
    if(!results.shouldCache || results.values==null) return;
    key = getKey(key, input[0].length);
    storeValue(key, new float[][][] {
      (float[][])Util.clone(input), (float[][])Util.clone(results.values)
    });

  }





    /** holds float arrays or DataCacheManager ids */
    private Hashtable map = new Hashtable();

    private void storeValue(String key, double[][][]value) {
        checkCache();
        Object object = value;
        if(useDataCacheManager) {
            removeValue(key);
            object = DataCacheManager.getCacheManager().addToCache("ArrayCache", value, true);
        }
        map.put(key, object);
    }

    private void storeValue(String key, float[][][]value) {
        checkCache();
        Object object = value;
        if(useDataCacheManager) {
            removeValue(key);
            object = DataCacheManager.getCacheManager().addToCache("ArrayCache", value, true);
        }
        map.put(key, object);
    }

    public void finalize() throws Throwable {
        super.finalize();
        if(useDataCacheManager) {
            //            System.err.println ("arraycache finalize");
            clearCache();
        }
    }


    private void checkCache() {
        if(map.size()>4) {
            clearCache();
        }
    }

    private void removeValue(Object key) {
        Object object  = map.get(key);
        if(object!=null) {
            if(useDataCacheManager)
                DataCacheManager.getCacheManager().removeFromCache(object);
            map.remove(key);
        }
    }

    private void clearCache() {
        for(Enumeration keys= map.keys();keys.hasMoreElements(); ) {
            Object key= keys.nextElement();
            Object object  = map.get(key);
            if(useDataCacheManager) {
                DataCacheManager.getCacheManager().removeFromCache(object);
            }
        }
        map = new Hashtable();
    }


    private double[][][] getDoubleValue(String key) {
        Object object = map.get(key);
        if(object==null) {
            return null;
        }
        if(useDataCacheManager)
            return DataCacheManager.getCacheManager().getDoubleArray3D(object);
        return (double[][][])object;
    }

    private float[][][] getFloatValue(String key) {
        Object object = map.get(key);
        if(object==null) {
            return null;
        }
        if(useDataCacheManager)
            return DataCacheManager.getCacheManager().getFloatArray3D(object);
        return (float[][][])object;
    }







    public static class DoubleResult {
        public boolean shouldCache = true;
        public double[][]values;

        public DoubleResult() {
            shouldCache = false;
            values = null;
        }

        public DoubleResult(boolean shouldCache) {
            this.shouldCache = shouldCache;
        }

        public DoubleResult(double[][]values) {
            this.values = values;
            this.shouldCache = true;
        }


        public double[][]cloneForCache(double[][]a) {
            if(!shouldCache) return null;
            return Util.clone(a);
        }

        public boolean getShouldCache() {
            return shouldCache;
        }

    }




    public static class FloatResult {
        public boolean shouldCache = true;
        public float[][]values;

        public FloatResult() {
            shouldCache = false;
            values = null;
        }

        public FloatResult(boolean shouldCache) {
            this.shouldCache = shouldCache;
        }

        public FloatResult(float[][]values) {
            this.values = values;
            this.shouldCache = true;
        }

        public float[][]cloneForCache(float[][]a) {
            if(!shouldCache) return null;
            return Util.clone(a);
        }

        public boolean getShouldCache() {
            return shouldCache;
        }

    }







}


 

Ads help maintain this website.