/******************************************************************************* * Copyright (c) 2007, 2010 Innoopract Informationssysteme GmbH. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Innoopract Informationssysteme GmbH - initial API and implementation * EclipseSource - ongoing development ******************************************************************************/ package org.eclipse.swt.internal.graphics; import java.io.*; import java.math.BigDecimal; import java.text.MessageFormat; import java.util.*; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.Point; public final class DefaultTextSizeStorage implements ITextSizeStorage { public static final int MIN_STORE_SIZE = 1000; public static final int DEFAULT_STORE_SIZE = 10000; static final String COMMENT = "RAP DefaultFontSizeStorage"; static final String PREFIX_FONT_KEY = "FONT_"; private static class Entry { private Point point; private long timeStamp; } private final Object lock; // access is guarded by 'lock' private final Set fontDatas; // access is guarded by 'lock' private final Map data; private int storeSize; private int clearRange; private int clock; public DefaultTextSizeStorage() { lock = new Object(); data = new HashMap(); fontDatas = new HashSet(); setStoreSize( DEFAULT_STORE_SIZE ); } ///////////////////////////// // interface IFontSizeStorage public FontData[] getFontList() { FontData[] result; synchronized( lock ) { result = new FontData[ fontDatas.size() ]; fontDatas.toArray( result ); } return result; } public void storeFont( final FontData fontData ) { synchronized( lock ) { fontDatas.add( fontData ); } } public Point lookupTextSize( final Integer key ) { Point result = null; synchronized( lock ) { Entry entry = ( Entry )data.get( key ); if( entry != null ) { entry.timeStamp = clock++; result = entry.point; } } result = defensiveCopy( result ); return result; } public void storeTextSize( final Integer key, final Point size ) { Point clone = defensiveCopy( size ); Entry entry = new Entry(); entry.point = clone; entry.timeStamp = clock++; synchronized( lock ) { data.put( key , entry ); handleOverFlow(); } } ////////////// // persistance public void save( final OutputStream stream ) throws IOException { Properties properties = new Properties(); FontData[] fontDataList; synchronized( lock ) { fontDataList = getFontList(); Iterator iterator = data.keySet().iterator(); while( iterator.hasNext() ) { Integer key = ( Integer )iterator.next(); Point size = ( ( Entry )data.get( key ) ).point; StringBuffer value = new StringBuffer(); value.append( size.x ); value.append( "," ); value.append( size.y ); properties.setProperty( key.toString(), value.toString() ); } } for( int i = 0; i < fontDataList.length; i++ ) { StringBuffer key = new StringBuffer(); key.append( PREFIX_FONT_KEY ); key.append( i ); String value = fontDataList[ i ].toString(); properties.setProperty( key.toString(), value ); } properties.store( stream, COMMENT ); } public void read( final InputStream stream ) throws IOException { Properties properties = new Properties(); properties.load( stream ); synchronized( lock ) { Enumeration keys = properties.keys(); while( keys.hasMoreElements() ) { String key = ( String )keys.nextElement(); String value = properties.getProperty( key ); if( key.startsWith( PREFIX_FONT_KEY ) ) { FontData fontData = new FontData( value ); storeFont( fontData ); } else { storeTextSize( new Integer( key ), parsePoint( value ) ); } } } } //////////////////// // overflow handling public void setStoreSize( final int storeSize ) { if( storeSize < MIN_STORE_SIZE ) { String txt = "Store size must be >= {0}."; Object[] param = new Object[] { new Integer( MIN_STORE_SIZE ) }; String msg = MessageFormat.format( txt, param ); throw new IllegalArgumentException( msg ); } BigDecimal ten = new BigDecimal( 10 ); BigDecimal bdStoreSize = new BigDecimal( storeSize ); int rounding = BigDecimal.ROUND_HALF_UP; clearRange = bdStoreSize.divide( ten, 0, rounding ).intValue(); this.storeSize = storeSize; } public int getStoreSize() { return storeSize; } private void handleOverFlow() { if( data.size() >= storeSize ) { Entry[] entries = new Entry[ data.size() ]; data.values().toArray( entries ); Arrays.sort( entries, new Comparator() { public int compare( final Object obj1, final Object obj2 ) { Entry e1 = ( Entry )obj1; Entry e2 = ( Entry )obj2; int result = 0; if( e1.timeStamp > e2.timeStamp ) { result = 1; } else if( e1.timeStamp < e2.timeStamp ) { result = -1; } return result; } } ); for( int i = 0; i < clearRange; i++ ) { data.values().remove( entries[ i ] ); } } } ////////////////// // helping methods private static Point parsePoint( final String value ) { String[] values = value.split( "," ); int x = Integer.valueOf( values[ 0 ] ).intValue(); int y = Integer.valueOf( values[ 1 ] ).intValue(); return new Point( x, y ); } private static Point defensiveCopy( final Point point ) { Point result = null; if( point != null ) { result = new Point( point.x, point.y ); } return result; } // for test purposes only void resetFontList() { synchronized( fontDatas ) { fontDatas.clear(); } } // for test purposes only void resetStringSizes() { synchronized( data ) { data.clear(); } } }