/*******************************************************************************
* Copyright (c) 2008, 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.rwt.internal.theme;
import java.util.*;
import org.eclipse.rwt.internal.theme.css.ConditionalValue;
import org.eclipse.rwt.internal.theme.css.StyleSheet;
/**
* Contains the values defined in a CSS style sheet in an optimized structure
* for providing quick access to the values for a given element and property.
*/
public final class ThemeCssValuesMap {
private final Map valuesMap;
public ThemeCssValuesMap( final StyleSheet styleSheet,
final ThemeableWidget[] themeableWidgets )
{
valuesMap = new HashMap();
extractValues( styleSheet, themeableWidgets );
}
public ConditionalValue[] getValues( final String elementName,
final String propertyName )
{
ConditionalValue[] result;
PropertyKey propertyKey = new PropertyKey( elementName, propertyName );
result = ( ConditionalValue[] )valuesMap.get( propertyKey );
// if element name is unknown, resort to * rules
if( result == null ) {
PropertyKey wildcardKey = new PropertyKey( "*", propertyName );
result = ( ConditionalValue[] )valuesMap.get( wildcardKey );
}
return result;
}
public QxType[] getAllValues() {
Set resultSet = new HashSet();
Iterator iterator = valuesMap.values().iterator();
while( iterator.hasNext() ) {
ConditionalValue[] condValues = ( ConditionalValue[] )iterator.next();
for( int i = 0; i < condValues.length; i++ ) {
ConditionalValue condValue = condValues[ i ];
resultSet.add( condValue.value );
}
}
QxType[] result = new QxType[ resultSet.size() ];
resultSet.toArray( result );
return result;
}
private void extractValues( final StyleSheet styleSheet,
final ThemeableWidget[] themeableWidgets )
{
for( int i = 0; i < themeableWidgets.length; i++ ) {
ThemeableWidget themeableWidget = themeableWidgets[ i ];
extractValuesForWidget( styleSheet, themeableWidget );
}
}
private void extractValuesForWidget( final StyleSheet styleSheet,
final ThemeableWidget themeableWidget )
{
IThemeCssElement[] elements = themeableWidget.elements;
if( elements != null ) {
for( int i = 0; i < elements.length; i++ ) {
IThemeCssElement element = elements[ i ];
String elementName = element.getName();
String[] properties = element.getProperties();
for( int j = 0; j < properties.length; j++ ) {
String propertyName = properties[ j ];
PropertyKey key = new PropertyKey( elementName, propertyName );
ConditionalValue[] values = styleSheet.getValues( elementName,
propertyName );
ConditionalValue[] filteredValues = filterValues( values, element );
valuesMap.put( key, filteredValues );
}
}
}
}
private ConditionalValue[] filterValues( final ConditionalValue[] values,
final IThemeCssElement element )
{
List resultList = new ArrayList();
String[] latestConstraint = null;
for( int j = 0; j < values.length; j++ ) {
ConditionalValue value = values[ j ];
if( !Arrays.equals( latestConstraint, value.constraints ) ) {
if( matches( element, value.constraints ) ) {
resultList.add( value );
latestConstraint = value.constraints;
}
}
}
ConditionalValue[] result = new ConditionalValue[ resultList.size() ];
resultList.toArray( result );
return result;
}
private static boolean matches( final IThemeCssElement element,
final String[] constraints )
{
boolean passed = true;
// TODO [rst] Revise: no restrictions for * rules
if( !"*".equals( element.getName() ) ) {
for( int k = 0; k < constraints.length && passed; k++ ) {
String constraint = constraints[ k ];
if( constraint.charAt( 0 ) == ':' ) {
passed &= contains( element.getStates(), constraint.substring( 1 ) );
} else if( constraint.charAt( 0 ) == '[' ) {
passed &= contains( element.getStyles(), constraint.substring( 1 ) );
}
}
}
return passed;
}
private static boolean contains( final String[] elements, final String string )
{
boolean result = false;
for( int i = 0; i < elements.length && !result; i++ ) {
if( string.equals( elements[ i ] ) ) {
result = true;
}
}
return result;
}
private static class PropertyKey {
private final String element;
private final String property;
private final int hashCode;
public PropertyKey( final String element, final String property ) {
this.element = element;
this.property = property;
hashCode = element.hashCode() ^ property.hashCode();
}
public boolean equals( final Object obj ) {
boolean result;
if( obj == this ) {
result = true;
} else if( obj != null && obj.getClass() == getClass() ) {
PropertyKey other = ( PropertyKey )obj;
result = element.equals( other.element )
&& property.equals( other.property );
} else {
result = false;
}
return result;
}
public int hashCode() {
return hashCode;
}
}
}