/*******************************************************************************
* Copyright (c) 2007, 2016 Innoopract Informationssysteme GmbH and others.
* 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.rap.rwt.internal.textsize;
import static org.eclipse.rap.rwt.internal.RWTProperties.getTextSizeStoreSize;
import java.io.Serializable;
import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Point;
public final class TextSizeStorage {
public static final int MIN_STORE_SIZE = 1000;
public static final int DEFAULT_STORE_SIZE = 10000;
private final Object lock;
// access is guarded by 'lock'
private final Set<FontData> fontDatas;
// access is guarded by 'lock'
private final Map<Integer,Entry> data;
private int maximumStoreSize;
private int clearRange;
private long clock;
private static class Entry {
private Point point;
private long timeStamp;
}
private static class EntryComparator implements Comparator<Entry>, Serializable {
@Override
public int compare( Entry entry1, Entry entry2 ) {
int result = 0;
if( entry1.timeStamp > entry2.timeStamp ) {
result = 1;
} else if( entry1.timeStamp < entry2.timeStamp ) {
result = -1;
}
return result;
}
}
public TextSizeStorage() {
lock = new Object();
data = new HashMap<>();
fontDatas = new HashSet<>();
setMaximumStoreSize( getTextSizeStoreSize( DEFAULT_STORE_SIZE ) );
}
FontData[] getFontList() {
FontData[] result;
synchronized( lock ) {
result = new FontData[ fontDatas.size() ];
fontDatas.toArray( result );
}
return result;
}
void storeFont( FontData fontData ) {
synchronized( lock ) {
fontDatas.add( fontData );
}
}
Point lookupTextSize( Integer key ) {
Point result = null;
synchronized( lock ) {
Entry entry = data.get( key );
if( entry != null ) {
updateTimestamp( entry );
result = entry.point;
}
}
result = defensiveCopy( result );
return result;
}
void storeTextSize( Integer key, Point size ) {
Entry entry = new Entry();
entry.point = defensiveCopy( size );
updateTimestamp( entry );
synchronized( lock ) {
data.put( key, entry );
handleOverFlow();
}
}
////////////////////
// overflow handling
void setMaximumStoreSize( int maximumStoreSize ) {
checkLowerStoreSizeBoundary( maximumStoreSize );
calculateClearRange( maximumStoreSize );
this.maximumStoreSize = maximumStoreSize;
}
int getMaximumStoreSize() {
return maximumStoreSize;
}
private void handleOverFlow() {
if( data.size() >= maximumStoreSize ) {
Entry[] entries = sortEntries();
for( int i = 0; i < clearRange; i++ ) {
data.values().remove( entries[ i ] );
}
}
}
private Entry[] sortEntries() {
Entry[] result = new Entry[ data.size() ];
data.values().toArray( result );
Arrays.sort( result, new EntryComparator() );
return result;
}
//////////////////
// helping methods
private static void checkLowerStoreSizeBoundary( int maximumStoreSize ) {
if( maximumStoreSize < MIN_STORE_SIZE ) {
Object[] param = { Integer.valueOf( MIN_STORE_SIZE ) };
String msg = MessageFormat.format( "Store size must be >= {0}.", param );
throw new IllegalArgumentException( msg );
}
}
private static Point defensiveCopy( Point point ) {
return point == null ? null : new Point( point.x, point.y );
}
private void updateTimestamp( Entry entry ) {
entry.timeStamp = clock++;
}
private void calculateClearRange( int maximumStoreSize ) {
BigDecimal ten = new BigDecimal( 10 );
BigDecimal bdStoreSize = new BigDecimal( maximumStoreSize );
clearRange = bdStoreSize.divide( ten, 0, BigDecimal.ROUND_HALF_UP ).intValue();
}
}