/** * Copyright (c) 2003-2009, Xith3D Project Group all rights reserved. * * Portions based on the Java3D interface, Copyright by Sun Microsystems. * Many thanks to the developers of Java3D and Sun Microsystems for their * innovation and design. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the 'Xith3D Project Group' nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) A * RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE */ package org.xith3d.utility.cache; import java.util.ArrayList; import java.util.HashMap; import org.jagatoo.datatypes.NamedObject; /** * Class that manages cached items. * * @author David Yazel */ public class Cache< T > implements NamedObject { public static final int FIND_ANY = 0; public static final int FIND_BEST = 1; public static final int FIND_EXACT = 2; public static final int FIND_FIRST = 3; private static HashMap< String, Cache< ? >> caches = new HashMap< String, Cache< ? >>(); private final String name; private final boolean shareable; private final ArrayList< CacheNode< T >> items; private int attempts = 0; private int hits = 0; public Cache( String name, boolean shareable ) { this.name = name; this.shareable = shareable; this.items = new ArrayList< CacheNode< T >>(); caches.put( name, this ); } public final String getName() { return ( name ); } @SuppressWarnings( "unchecked" ) public static Cache< ? > getCache( Class< ? > classType, boolean shareable ) { String key = classType.getName() + shareable; Cache< ? > c = caches.get( key ); if ( c == null ) { c = new Cache( key, shareable ); } return ( c ); } /** * @return the object containing the object. * * @param o */ private CacheNode< T > findObject( T o ) { for ( CacheNode< T > cn: items ) { if ( cn.o == o ) { return ( cn ); } } return ( null ); } public synchronized void put( T o ) { CacheNode< T > n = findObject( o ); // if we already have an entry for this node then update its // statistics. if ( n != null ) { if ( shareable ) { int num = n.getNumUsers(); if ( num >= 0 ) { n.setNumUsers( num - 1 ); } n.setTimeLastAccessed( System.currentTimeMillis() ); } } else { n = new CacheNode< T >( o ); n.setNumUsers( 0 ); n.setTimeCreated( System.currentTimeMillis() ); items.add( n ); } } private CacheNode< T > find( CacheMatchInterface< T > matcher, int findStyle ) { CacheNode< T > best = null; float bestVal = 0; for ( CacheNode< T > cn: items ) { if ( matcher != null ) { float m = matcher.match( cn.o ); if ( m == 1 ) { return ( cn ); } else if ( m > 0 ) { if ( findStyle == FIND_FIRST ) { return ( cn ); } if ( m > bestVal ) { bestVal = m; best = cn; } } } else { return ( cn ); } } if ( findStyle == FIND_EXACT ) { return ( null ); } return ( best ); } private T get( CacheMatchInterface< T > matcher, int findStyle ) { attempts++; CacheNode< T > n = find( matcher, findStyle ); if ( n == null ) { return ( null ); } if ( shareable ) { n.setNumRequested( n.getNumRequested() + 1 ); n.setNumUsers( n.getNumUsers() + 1 ); n.setTimeLastAccessed( System.currentTimeMillis() ); } else { items.remove( n ); } hits++; return ( n.o ); } public synchronized T getBest( CacheMatchInterface< T > matcher ) { return ( get( matcher, FIND_BEST ) ); } public synchronized T getFirst( CacheMatchInterface< T > matcher ) { return ( get( matcher, FIND_FIRST ) ); } public synchronized T getAny() { return ( get( null, FIND_ANY ) ); } /** * Prints out the cache information to the log. */ @SuppressWarnings( "unchecked" ) public synchronized void dumpCacheInfo( boolean printItems ) { System.out.println( "Cache : " + name ); long totalMemory = 0; for ( CacheNode< T > cn: items ) { if ( cn.o instanceof Cachable ) { totalMemory += ( (Cachable< T >)cn.o ).memoryUsed(); if ( printItems ) { System.out.println( " " + ( (Cachable< T >)cn.o ).getName() + " " + ( (Cachable< T >)cn.o ).memoryUsed() + " bytes" ); } } } System.out.println( " " + items.size() + " items using " + totalMemory + " bytes" ); System.out.println( " " + attempts + " attempts with " + hits + " hits (" + ( (float)hits / (float)attempts ) + "%)" ); } public static void dumpAllCacheInfo() { for ( Cache< ? > c: caches.values() ) { c.dumpCacheInfo( true ); } } }