package net.xoetrope.xui.style;
import java.io.BufferedReader;
import java.util.Enumeration;
import java.util.Hashtable;
import java.awt.Font;
import net.xoetrope.debug.DebugLogger;
import net.xoetrope.xml.XmlElement;
import net.xoetrope.xml.XmlSource;
import net.xoetrope.xui.XProject;
import net.xoetrope.xui.build.BuildProperties;
/**
* Class for managing XStyles. XStyles are created
* externally and added to the hashtable. Also handles the retrieval of styles.
* <p>Copyright (c) Xoetrope Ltd., 1998-2004<br>
* $Revision: 2.7 $
*/
public class XStyleManager
{
/**
* Hashtable of project styles
*/
protected Hashtable styles;
/**
* Used to merge child styles with their parents
*/
protected Hashtable mergedStyles;
/**
* Fonts found when creating styles
*/
protected Hashtable fontCache;
/**
* The base style from which all others will be inherited
*/
protected static XStyle baseStyle = new XStyleEx();
/**
* The XProject for which this instance of the XStyleManager is working
*/
protected XProject currentProject;
/**
* Mapping of font attributes
*/
protected Hashtable fontMap;
/**
* Construct a new style manager.
* @param project the owner project
*/
public XStyleManager( XProject project )
{
this( project, 10 );
}
/**
* Construct a new style manager with an initial size.
* @param styleCount The number of initial styles in the styles Hashtable
*/
public XStyleManager( XProject project, int styleCount )
{
currentProject = project;
styles = new Hashtable( styleCount );
mergedStyles = new Hashtable( styleCount );
fontCache = new Hashtable( 10 );
try {
String styleFile = currentProject.getStartupParam( "StyleFile" );
if ( styleFile == null )
styleFile = "styles.xml";
load( styleFile );
}
catch ( Exception ex ) {
DebugLogger.logWarning( "No style file loaded!" );
}
}
/**
* Add a new XStyle to the hashtable with a unique name
* @param name The name of the style
* @param newStyle The XStyle to be associated with the passed name
*/
public void addStyle( String name, XStyle newStyle )
{
styles.remove( name );
styles.put( name, newStyle );
}
/**
* Retrieve a named XStyle from the hashtable. loop thru the "/" character
* and merge each one found with a new XStyle.
* @param name The name of the style.
* @param create booean to indicate whether the style should be created.
* @return The referenced XStyle
*/
public XStyle getStyle( String name, boolean create )
{
String styleName;
String qualifiedName = null;
qualifiedName = name;
if ( qualifiedName == null )
return null;
XStyle tempXStyle = (XStyle)mergedStyles.get( qualifiedName );
if ( tempXStyle != null )
return tempXStyle;
int startPos = 0;
if ( create ) {
tempXStyle = (XStyle)baseStyle.clone();
int pos;
do {
pos = qualifiedName.indexOf( "/", startPos );
if ( pos != -1 )
styleName = qualifiedName.substring( 0, pos );
else
styleName = qualifiedName;
XStyle targetStyle = (XStyle)styles.get( styleName );
if ( targetStyle instanceof XStyleEx ) {
if ( !( tempXStyle instanceof XStyleEx ))
tempXStyle = new XStyleEx( tempXStyle );
}
tempXStyle.mergeStyle( targetStyle );
startPos = pos + 1;
}
while ( pos != -1 );
mergedStyles.put( qualifiedName, tempXStyle );
}
return tempXStyle;
}
/**
* Check if a style is available, either already processed, or named somewhere
* in the raw styles file. The method checks only basic style names
* @param styleName the name of the style
* @return true if the style is found
*/
public boolean hasStyle( String styleName )
{
if ( mergedStyles.get( styleName ) != null )
return true;
else if ( styles.get( styleName ) != null )
return true;
return false;
}
/**
* Retrieve a named XStyle from the hashtable. loop thru the "/" character
* and merge each one found with a new XStyle.
* @param name The name of the style.
* @return The referenced XStyle
*/
public XStyle getStyle( String name )
{
return getStyle( name, true );
}
/**
* Get the font for a given style. Use the bold and italic attributes and do a
* bitwise or using the Font values to set the style.
* @param style the name of the style
* @return The constructed Font Object for the named XStyle
*/
public Font getFont( String style )
{
return getFont( getStyle( style ));
}
/**
* Get the font for a given style. Use the bold and italic attributes and do a
* bitwise or using the Font values to set the style.
* @param style the style
* @return The constructed Font Object for the passed XStyle
*/
public Font getFont( XStyle style )
{
Font f = (Font)fontCache.get( style.getFontHashcode());
if ( f != null )
return f;
int bold = style.getStyleAsInt( XStyle.FONT_WEIGHT );
int italic = style.getStyleAsInt( XStyle.FONT_ITALIC );
int fontStyle = 0;
if ( bold == 1 )
fontStyle = Font.BOLD;
if ( italic == 1 )
fontStyle = fontStyle | Font.ITALIC;
String fontFace = style.getStyleAsString( XStyle.FONT_FACE );
if ( fontFace == null )
return null;
int fontSize = style.getStyleAsInt( XStyle.FONT_SIZE );
// Now check the font mapping
// First the font face and font size
if ( fontMap != null ) {
String mappedFontFace = (String)fontMap.get( fontFace + fontSize );
if ( mappedFontFace != null )
fontFace = mappedFontFace;
else {
// Then just the face
mappedFontFace = (String)fontMap.get( fontFace );
if ( mappedFontFace != null )
fontFace = mappedFontFace;
}
// Split out the font size if necessary
int pos = fontFace.indexOf( ';' );
if ( pos > 0 ) {
fontSize = new Integer( fontFace.substring( pos + 1 )).intValue();
fontFace = fontFace.substring( 0, pos );
}
}
f = new Font( fontFace, fontStyle, fontSize );
fontCache.put( style.getFontHashcode(), f );
return f;
}
/**
* Setup the font mapping for
* @param fontMaps the font mappings (face and size or just face to face)
*/
public void setFontMap( Hashtable fontMaps )
{
fontCache = new Hashtable();
fontMap = new Hashtable();
Enumeration keys = fontMaps.keys();
while( keys.hasMoreElements()) {
String key = (String)keys.nextElement();
if ( key.startsWith( "font_" ))
fontMap.put( key.substring( 5 ), fontMaps.get( key ));
}
}
/**
* Load the styles from the specified file
* @param file the file name of the file to load
*/
public void load( String file )
{
BufferedReader r = null;
try {
r = currentProject.getBufferedReader( file, null );
if ( r != null ) {
XmlElement element = XmlSource.read( r );
loadXStyle( element, null );
baseStyle = getStyle( "base" );
}
}
catch ( Exception ex ) {
if ( BuildProperties.DEBUG )
DebugLogger.trace( "Error loading styles file (" + file + "), please check that the file exists. Usually it is found in the resources folder of the project." );
}
}
/**
* Load a style from the XML element and save it to the specified path in the
* style hierarchy
* @param element the source element
* @param path the save path
*/
public void loadXStyle( XmlElement element, String path )
{
Enumeration e = element.getChildren().elements();
XStyle xstyle = /*"true".equals( element.getAttribute( "extended" )) ?*/ new XStyleEx()/* : new XStyle()*/;
/**
* @todo set the number of styles based upon the number of children instead
* of adding them one by one. The addStle method needs to be modified on this
* basis also. (see XStyle and XStyleEx)
*/
while ( e.hasMoreElements() ) {
XmlElement eleStyle = ( XmlElement )e.nextElement();
String elementName = eleStyle.getName();
if ( elementName.equals( "style" )) {
String temp = element.getAttribute( "name" );
if (( path != null ) && ( path.length() > 0 ))
temp = path + "/" + element.getAttribute( "name" );
loadXStyle( eleStyle, temp );
}
else
xstyle.setStyle( xstyle.getStyleIndex( elementName ), eleStyle.getAttribute( "value" ) );
}
xstyle.setClosed( true );
String styleName = element.getAttribute( "name" );
if (( path != null ) && ( path.length() > 0 ))
styleName = path + "/" + styleName;
if (( styleName != null ) && ( styleName.length() > 0 ))
addStyle( styleName, xstyle );
}
/**
* Get the table of styles
* @return the stytle table
*/
public Hashtable getStyles()
{
return styles;
}
/**
* Get the parent of the named style
* @param name the style name
* @return the parent style
*/
public XStyle getStyleParent( String name )
{
if ( name.indexOf( "/" ) > 0 )
return getStyle( "base/" + name.substring( 0, name.lastIndexOf( "/" ) ) );
return null;
}
/**
* reset the styles in case a new stylesheet is to be loaded.
*/
public void reset()
{
styles.clear();
mergedStyles.clear();
fontCache.clear();
}
}