/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2007-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* 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
* Lesser General Public License for more details.
*/
package org.geotools.util;
import java.util.Set;
import org.geotools.factory.FactoryRegistryException;
import org.geotools.factory.GeoTools;
import org.geotools.factory.Hints;
import org.geotools.metadata.iso.citation.Citations;
import org.opengis.metadata.citation.Citation;
import org.opengis.util.GenericName;
/**
* This is facade around several constructs used by GeoTools for internal caching.
* <p>
* This class provides the following services:
* <ul>
* <li>Access to an implementation of "weak", "all" and "none" implementations of {@link ObjectCache}.</li>
* <li>The ability to turn a "code" into a good "key" for use with an ObjectCache.</li>
* <li>A Pair data object (think of C STRUCT) for use as a key when storing a value against two objects.</li>
* </ul>
*
* @since 2.5
* @author Jody Garnett
* @author Cory Horner
*
*
* @source $URL$
*/
public final class ObjectCaches {
/**
* Do not allow instantiation of this class.
*/
private ObjectCaches() {
}
/**
* A pair of Codes for {@link ObjectCache) to work with.
* Please be advised that this is a data object:
*
* <ul>
* <li>equals - is dependent on both source and target being equal.</li>
* <li>hashcode - is dependent on the hashCode of source and target.</li>
* </ul>
*
* A Pair is considered ordered:
*
* <blockquote><pre>
* Pair pair1 = new Pair("a","b");
* Pair pair2 = new Pair("b","a");
*
* System.out.println( pair1.equals( pair2 ) ); // prints false
* </pre></blockquote>
*
* {@link #createFromCoordinateReferenceSystemCodes}.
*/
private static final class Pair {
private final String source, target;
public Pair(String source, String target) {
this.source = source;
this.target = target;
}
public int hashCode() {
int code = 0;
if (source!=null) code = source.hashCode();
if (target!=null) code += target.hashCode()*37;
return code;
}
public boolean equals(final Object other) {
if (other instanceof Pair) {
final Pair that = (Pair) other;
return Utilities.equals(this.source, that.source) &&
Utilities.equals(this.target, that.target);
}
return false;
}
public String toString() {
return source + " \u21E8 " + target;
}
}
/**
* Create a two level cache, operates as a level1 cache that is willing to
* obtain values from a (usually shared) level2 cache.
* <p>
* This functionality is used to tie two ObjectCache implementations together
* (allowing them to collaborate while focusing on different use cases).
* The real world example of chaining is in {@link AbstractFindableAuthorityFactory} in which:
* <ul>
* <li>create uses: chain( cache, findCache )
* <li>find uses: chain( findCache, cache )
* </ul>
* In this manner the find operation does not upset normal cache. It will not create any
* objects already present in the cache.
*
* @param level1
* @param level2
* @return ObjectCache
*/
public static ObjectCache chain( final ObjectCache level1, final ObjectCache level2 ){
if ( level1 == level2 ) {
return level1;
}
if( level1 == null ) return level2;
if( level2 == null ) return level1;
return new ObjectCache(){
public void clear() {
level1.clear();
}
public Object get( Object key ) {
Object value = level1.get( key );
if( value == null ){
Object check = level2.get( key );
if( check != null ) {
try {
level1.writeLock(key);
value = level1.peek(key);
if( value == null ){
level1.put(key, check );
value = check;
}
}
finally {
level1.writeUnLock(key);
}
}
}
return value;
}
public Object peek( Object key ) {
return level1.peek(key);
}
public void put( Object key, Object object ) {
level1.put(key, object );
}
public void writeLock( Object key ) {
level1.writeLock(key);
}
public void writeUnLock( Object key ) {
level1.writeLock(key);
}
public Set<Object> getKeys(){
return level1.getKeys();
}
public void remove(Object key){
level1.remove(key);
}
};
}
/**
* Utility method used to produce cache based on provide Hint
*/
public static ObjectCache create( Hints hints )
throws FactoryRegistryException {
if( hints == null ) hints = GeoTools.getDefaultHints();
String policy = (String) hints.get(Hints.CACHE_POLICY);
int limit = Hints.CACHE_LIMIT.toValue(hints);
return create( policy, limit );
}
/**
* Utility method used to produce an ObjectCache.
*
* @param policy One of "weak", "all", "none", "soft"
* @param size Used to indicate requested size, exact use depends on policy
* @return A new ObjectCache
* @see Hints.BUFFER_POLICY
*/
public static ObjectCache create( String policy, int size ){
if ("weak".equalsIgnoreCase(policy)) {
return new WeakObjectCache(0);
} else if ("all".equalsIgnoreCase(policy)) {
return new DefaultObjectCache(size);
} else if ("none".equalsIgnoreCase(policy)) {
return NullObjectCache.INSTANCE;
} else if ("fixed".equalsIgnoreCase(policy)) {
return new FixedSizeObjectCache(size);
} else if ("soft".equals(policy)){
return new SoftObjectCache(size);
} else {
return new DefaultObjectCache(size);
}
}
/**
* Produce a good key based on the privided citaiton and code.
* You can think of the citation as being "here" and the code being the "what".
*
* @param code Code
* @return A good key for use with ObjectCache
*/
public static String toKey( Citation citation, String code ){
code = code.trim();
final GenericName name = NameFactory.create(code);
final GenericName scope = name.scope().name();
if (scope == null) {
return code;
}
if (citation != null && Citations.identifierMatches( citation, scope.toString())) {
return name.tip().toString().trim();
}
return code;
}
/**
* Produce a good key based on a pair of codes.
*
* @param code1
* @param code2
* @return A object to use as a key
*/
public static Object toKey( Citation citation, String code1, String code2 ){
String key1 = toKey( citation, code1 );
String key2 = toKey( citation, code2 );
return new Pair( key1, key2 );
}
}