001    // Copyright 2006-2007 Daniel Gredler
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package net.sf.beanform.util;
016    
017    import java.util.HashMap;
018    import java.util.Map;
019    
020    /**
021     * A thread-safe cache of strings that allows aliasing of long strings to smaller
022     * strings that can then be used to reverse the process.
023     *
024     * @author Daniel Gredler
025     */
026    public class StringCache {
027    
028        protected final static char[] CHARS = {
029            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
030            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
031            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
032    
033        protected int maxSize;
034        protected Map<String, String> cacheByShort;
035        protected Map<String, String> cacheByReal;
036    
037        public StringCache( int maxSize ) {
038            this.maxSize = maxSize;
039            this.cacheByShort = new HashMap<String, String>( maxSize );
040            this.cacheByReal = new HashMap<String, String>( maxSize );
041        }
042    
043        public synchronized String getShortVersion( String real ) {
044            if( this.cacheByReal.size() == this.maxSize ) return real;
045            String s;
046            if( this.cacheByReal.containsKey( real ) ) {
047                s = this.cacheByReal.get( real );
048            }
049            else {
050                s = Character.toString( CHARS[ 0 ] );
051                for( int i = 1; this.cacheByShort.containsKey( s ); i++ ) {
052                    s = getChars( CHARS, i );
053                }
054                this.cacheByShort.put( s, real );
055                this.cacheByReal.put( real, s );
056            }
057            return s;
058        }
059    
060        public synchronized String getRealVersion( String shortVersion ) {
061            String real;
062            if( this.cacheByShort.containsKey( shortVersion ) ) {
063                real = this.cacheByShort.get( shortVersion );
064            }
065            else {
066                real = shortVersion;
067            }
068            return real;
069        }
070    
071        protected static String getChars( char[] chars, int index ) {
072            String s = new String();
073            int i = index / chars.length;
074            if( i > 0 ) {
075                s = getChars( chars, i - 1 ) + s;
076            }
077            s = s + chars[ index % chars.length ];
078            return s;
079        }
080    
081    }