Home | History | Annotate | Download | only in io
      1 /*
      2  * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
      3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      4  *
      5  * This code is free software; you can redistribute it and/or modify it
      6  * under the terms of the GNU General Public License version 2 only, as
      7  * published by the Free Software Foundation.  Oracle designates this
      8  * particular file as subject to the "Classpath" exception as provided
      9  * by Oracle in the LICENSE file that accompanied this code.
     10  *
     11  * This code is distributed in the hope that it will be useful, but WITHOUT
     12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     14  * version 2 for more details (a copy is included in the LICENSE file that
     15  * accompanied this code).
     16  *
     17  * You should have received a copy of the GNU General Public License version
     18  * 2 along with this work; if not, write to the Free Software Foundation,
     19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     20  *
     21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     22  * or visit www.oracle.com if you need additional information or have any
     23  * questions.
     24  */
     25 
     26 /*
     27  */
     28 
     29 package java.io;
     30 
     31 import java.util.Iterator;
     32 import java.util.Map;
     33 import java.util.LinkedHashMap;
     34 import java.util.Set;
     35 
     36 class ExpiringCache {
     37     private long millisUntilExpiration;
     38     private Map<String,Entry> map;
     39     // Clear out old entries every few queries
     40     private int queryCount;
     41     private int queryOverflow = 300;
     42     private int MAX_ENTRIES = 200;
     43 
     44     static class Entry {
     45         private long   timestamp;
     46         private String val;
     47 
     48         Entry(long timestamp, String val) {
     49             this.timestamp = timestamp;
     50             this.val = val;
     51         }
     52 
     53         long   timestamp()                  { return timestamp;           }
     54         void   setTimestamp(long timestamp) { this.timestamp = timestamp; }
     55 
     56         String val()                        { return val;                 }
     57         void   setVal(String val)           { this.val = val;             }
     58     }
     59 
     60     ExpiringCache() {
     61         this(30000);
     62     }
     63 
     64     @SuppressWarnings("serial")
     65     ExpiringCache(long millisUntilExpiration) {
     66         this.millisUntilExpiration = millisUntilExpiration;
     67         map = new LinkedHashMap<String,Entry>() {
     68             // Android-changed: Qualified ExpiringCache.Entry to distinguish from Map.Entry.
     69             // There seems to be a compiler difference between javac and jack here;
     70             // Map.Entry<String,Entry> doesn't work on jack since the latter "Entry" gets
     71             // interpreted as referring to Map.Entry rather than ExpiringCache.Entry.
     72             // protected boolean removeEldestEntry(Map.Entry<String,Entry> eldest) {
     73             protected boolean removeEldestEntry(Map.Entry<String,ExpiringCache.Entry> eldest) {
     74               return size() > MAX_ENTRIES;
     75             }
     76           };
     77     }
     78 
     79     synchronized String get(String key) {
     80         if (++queryCount >= queryOverflow) {
     81             cleanup();
     82         }
     83         Entry entry = entryFor(key);
     84         if (entry != null) {
     85             return entry.val();
     86         }
     87         return null;
     88     }
     89 
     90     synchronized void put(String key, String val) {
     91         if (++queryCount >= queryOverflow) {
     92             cleanup();
     93         }
     94         Entry entry = entryFor(key);
     95         if (entry != null) {
     96             entry.setTimestamp(System.currentTimeMillis());
     97             entry.setVal(val);
     98         } else {
     99             map.put(key, new Entry(System.currentTimeMillis(), val));
    100         }
    101     }
    102 
    103     synchronized void clear() {
    104         map.clear();
    105     }
    106 
    107     private Entry entryFor(String key) {
    108         Entry entry = map.get(key);
    109         if (entry != null) {
    110             long delta = System.currentTimeMillis() - entry.timestamp();
    111             if (delta < 0 || delta >= millisUntilExpiration) {
    112                 map.remove(key);
    113                 entry = null;
    114             }
    115         }
    116         return entry;
    117     }
    118 
    119     private void cleanup() {
    120         Set<String> keySet = map.keySet();
    121         // Avoid ConcurrentModificationExceptions
    122         String[] keys = new String[keySet.size()];
    123         int i = 0;
    124         for (String key: keySet) {
    125             keys[i++] = key;
    126         }
    127         for (int j = 0; j < keys.length; j++) {
    128             entryFor(keys[j]);
    129         }
    130         queryCount = 0;
    131     }
    132 }
    133