/**
* Copyright (C) 2009-2014 Cars and Tracks Development Project (CTDP).
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package net.ctdp.rfdynhud.widgets;
import java.awt.Color;
import java.awt.Font;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.ctdp.rfdynhud.gamedata.GameResolution;
import net.ctdp.rfdynhud.gamedata.LiveGameData;
import net.ctdp.rfdynhud.gamedata.__GDPrivilegedAccess;
import net.ctdp.rfdynhud.input.InputMappings;
import net.ctdp.rfdynhud.properties.AbstractPropertiesKeeper;
import net.ctdp.rfdynhud.properties.BackgroundProperty;
import net.ctdp.rfdynhud.properties.BooleanProperty;
import net.ctdp.rfdynhud.properties.BorderProperty;
import net.ctdp.rfdynhud.properties.ColorProperty;
import net.ctdp.rfdynhud.properties.FlatPropertiesContainer;
import net.ctdp.rfdynhud.properties.FontProperty;
import net.ctdp.rfdynhud.properties.PropertiesContainer;
import net.ctdp.rfdynhud.properties.PropertiesKeeper;
import net.ctdp.rfdynhud.properties.Property;
import net.ctdp.rfdynhud.properties.PropertyLoader;
import net.ctdp.rfdynhud.util.FontUtils;
import net.ctdp.rfdynhud.util.PropertyWriter;
import net.ctdp.rfdynhud.util.WidgetZYXComparator;
import net.ctdp.rfdynhud.widgets.base.widget.StatefulWidget;
import net.ctdp.rfdynhud.widgets.base.widget.Widget;
import net.ctdp.rfdynhud.widgets.base.widget.__WPrivilegedAccess;
import org.openmali.vecmath2.util.ColorUtils;
/**
* The {@link WidgetsConfiguration} handles the drawing of all visible widgets.
*
* @author Marvin Froehlich (CTDP)
*/
public class WidgetsConfiguration implements PropertiesKeeper
{
public static interface ConfigurationLoadListener
{
public void beforeWidgetsConfigurationCleared( WidgetsConfiguration widgetsConfig, LiveGameData gameData, boolean isEditorMode );
public void afterWidgetsConfigurationLoaded( WidgetsConfiguration widgetsConfig, LiveGameData gameData, boolean isEditorMode );
}
private int id = 0;
private String name = null;
private final List<Widget> widgets = new ArrayList<Widget>();
private final Map<String, Widget> widgetsMap = new HashMap<String, Widget>();
private final Map<String, Color> colorMap = new HashMap<String, Color>();
private final Map<String, Font> fontMap = new HashMap<String, Font>();
private final Map<String, String> fontStringMap = new HashMap<String, String>();
private final Map<String, Boolean> fontVirtualMap = new HashMap<String, Boolean>();
private final Map<String, String> borderMap = new HashMap<String, String>();
//@SuppressWarnings( "unchecked" )
//private final Map<Class<? extends StatefulWidget>, Object> generalStores = new HashMap<Class<? extends StatefulWidget>, Object>();
private final Map<String, Object> localStores = new HashMap<String, Object>();
private final Map<String, Boolean> visibilities = new HashMap<String, Boolean>();
private final BooleanProperty useClassScoring = new BooleanProperty( "useClassScoring", "useClassScoring", false, false );
private boolean needsCheckFixAndBake = true;
private final GameResolution gameResolution = new GameResolution();
private InputMappings inputMappings = null;
private boolean isValid = false;
public final int getId()
{
return ( id );
}
public final String getName()
{
return ( name );
}
void setValid( boolean valid )
{
this.isValid = valid;
}
public final boolean isValid()
{
return ( isValid );
}
private static String getLocalStoreKey( Widget widget )
{
return ( widget.getClass().getName() + "::" + widget.getName() );
}
/**
* Finds a free name starting with 'baseName'.
*
* @param baseName the name prefix
*
* @return the found free name.
*/
public String findFreeName( String baseName )
{
for ( int i = 1; i < Integer.MAX_VALUE; i++ )
{
String name = baseName + i;
boolean isFree = true;
for ( int j = 0; j < widgets.size(); j++ )
{
if ( name.equals( widgets.get( j ).getName() ) )
{
isFree = false;
break;
}
}
if ( isFree )
return ( name );
}
// Theoretically unreachable code!
return ( null );
}
void sortWidgets()
{
Collections.sort( widgets, WidgetZYXComparator.INSTANCE );
}
/**
* Adds a new {@link Widget} to be drawn by this manager.
*
* @param widget
* @param isLoading
* @param gameData
*/
void addWidget( Widget widget, boolean isLoading, LiveGameData gameData )
{
widgets.add( widget );
widgetsMap.put( widget.getName(), widget );
__WPrivilegedAccess.setConfiguration( this, widget, isLoading, gameData );
sortWidgets();
}
/**
* Removes a {@link Widget} from the drawing process.
*
* @param widget
* @param gameData
*/
@SuppressWarnings( "rawtypes" )
void removeWidget( Widget widget, LiveGameData gameData )
{
widgets.remove( widget );
widgetsMap.remove( widget.getName() );
if ( ( widget instanceof StatefulWidget ) && __WPrivilegedAccess.hasLocalStore( (StatefulWidget)widget ) )
{
localStores.put( getLocalStoreKey( widget ), ( (StatefulWidget)widget ).getLocalStore() );
//if ( !isEditorMode )
// visibilities.put( getLocalStoreKey( widget ), widget.isInputVisible() );
}
if ( widget.getConfiguration() != null )
__WPrivilegedAccess.setConfiguration( null, widget, false, gameData );
}
/**
* Removes all {@link Widget}s and clears all name- and alias maps.
*/
@SuppressWarnings( "rawtypes" )
void clear( LiveGameData gameData, boolean isEditorMode, ConfigurationLoadListener clearListener )
{
if ( clearListener != null )
clearListener.beforeWidgetsConfigurationCleared( this, gameData, isEditorMode );
//localStores.clear();
for ( int i = 0; i < widgets.size(); i++ )
{
Widget widget = widgets.get( i );
widget.beforeConfigurationCleared( this, gameData, isEditorMode );
}
if ( !isEditorMode )
{
for ( int i = 0; i < widgets.size(); i++ )
{
Widget widget = widgets.get( i );
if ( ( widget instanceof StatefulWidget ) && __WPrivilegedAccess.hasLocalStore( (StatefulWidget)widget ) )
localStores.put( getLocalStoreKey( widget ), ( (StatefulWidget)widget ).getLocalStore() );
visibilities.put( getLocalStoreKey( widget ), widget.getInputVisibility() );
__WPrivilegedAccess.setConfiguration( null, widget, false, gameData );
}
}
widgets.clear();
widgetsMap.clear();
colorMap.clear();
fontMap.clear();
fontStringMap.clear();
fontVirtualMap.clear();
borderMap.clear();
this.name = null;
}
boolean updateNameMapping( Widget widget, String oldName )
{
if ( widgetsMap.get( oldName ) == widget )
{
widgetsMap.remove( oldName );
widgetsMap.put( widget.getName(), widget );
return ( true );
}
return ( false );
}
/**
* Gets the number of {@link Widget}s in this manager.
*
* @return the number of {@link Widget}s in this manager.
*/
public final int getNumWidgets()
{
return ( widgets.size() );
}
/**
* Gets the index-th {@link Widget} from this manager.
*
* @param index the Widget's index
*
* @return the index-th {@link Widget} from this manager.
*/
public final Widget getWidget( int index )
{
return ( widgets.get( index ) );
}
/**
* Gets the {@link Widget} with the specified name from this manager.
*
* @param name the Widget's name
*
* @return the {@link Widget} with the specified name from this manager.
*/
public final Widget getWidget( String name )
{
return ( widgetsMap.get( name ) );
}
/**
* Searches the {@link Widget}s in the {@link WidgetsConfiguration} for those of the passed type.
*
* @param <W> the return type
* @param clazz the return type restriction
* @param includeSubclasses whether to search for the specific class only or also for subclasses of it
*
* @return the first matching {@link Widget} or <code>null</code>.
*/
@SuppressWarnings( "unchecked" )
public final <W extends Widget> W getWidgetByClass( Class<W> clazz, boolean includeSubclasses )
{
int n = getNumWidgets();
if ( includeSubclasses )
{
for ( int i = 0; i < n; i++ )
{
Widget w = getWidget( i );
if ( clazz.isAssignableFrom( w.getClass() ) )
return ( (W)w );
}
}
else
{
for ( int i = 0; i < n; i++ )
{
Widget w = getWidget( i );
if ( clazz == w.getClass() )
return ( (W)w );
}
}
return ( null );
}
/**
* Sets the dirty flags on all {@link Widget}s.
*/
public void setAllDirtyFlags()
{
for ( int i = 0; i < getNumWidgets(); i++ )
{
getWidget( i ).setDirtyFlag();
}
}
void setAllWidgetsDirty()
{
int n = getNumWidgets();
for ( int i = 0; i < n; i++ )
{
getWidget( i ).forceAndSetDirty( true );
}
}
void setInputMappings( InputMappings inputMappings )
{
this.inputMappings = inputMappings;
}
public final InputMappings getInputMappings()
{
return ( inputMappings );
}
@SuppressWarnings( "rawtypes" )
void setJustLoaded( LiveGameData gameData, boolean isEditorMode, String name, ConfigurationLoadListener loadListener )
{
this.id++;
this.name = name;
this.needsCheckFixAndBake = true;
for ( int i = 0; i < widgets.size(); i++ )
{
Widget widget = widgets.get( i );
if ( widget instanceof StatefulWidget )
{
Object localStore = localStores.get( getLocalStoreKey( widget ) );
if ( localStore != null )
{
__WPrivilegedAccess.setLocalStore( localStore, (StatefulWidget)widget );
}
}
if ( !isEditorMode )
{
Boolean visibility = visibilities.get( getLocalStoreKey( widget ) );
if ( visibility != null )
{
__WPrivilegedAccess.setInputVisible( widget, visibility );
}
}
}
for ( int i = 0; i < widgets.size(); i++ )
{
Widget widget = widgets.get( i );
widget.afterConfigurationLoaded( this, gameData, isEditorMode );
}
if ( loadListener != null )
loadListener.afterWidgetsConfigurationLoaded( this, gameData, isEditorMode );
}
private void fixVirtualNamedFonts()
{
for ( String name : fontMap.keySet() )
{
Boolean virtual = fontVirtualMap.get( name );
if ( virtual == Boolean.TRUE )
{
fontMap.put( name, FontUtils.parseFont( fontStringMap.get( name ), gameResolution.getViewportHeight(), false, true ) );
}
}
resetAllFontProperties();
}
boolean setViewport( int x, int y, int w, int h )
{
if ( __GDPrivilegedAccess.setViewport( x, y, w, h, gameResolution ) )
{
int n = getNumWidgets();
for ( int i = 0; i < n; i++ )
{
Widget widget = getWidget( i );
widget.forceReinitialization();
widget.forceCompleteRedraw( true );
if ( widget.getPosition().isBaked() )
widget.bake( false );
}
fixVirtualNamedFonts();
return ( true );
}
return ( false );
}
public final GameResolution getGameResolution()
{
return ( gameResolution );
}
/**
* Checks, if all Widgets are within the game's bounds.
* If not, they are moved and possibly resized to be in bounds.
*
* @param isEditorMode editor mode?
*/
void checkFixAndBakeConfiguration( boolean isEditorMode )
{
if ( !needsCheckFixAndBake )
return;
final int gameResX = gameResolution.getViewportWidth();
final int gameResY = gameResolution.getViewportHeight();
int n = getNumWidgets();
for ( int i = 0; i < n; i++ )
{
Widget w = getWidget( i );
if ( w.getPosition().getEffectiveX() < 0 )
{
if ( w.getPosition().getEffectiveY() < 0 )
w.getPosition().setEffectivePosition( 0, 0 );
else
w.getPosition().setEffectivePosition( 0, w.getPosition().getEffectiveY() );
}
else if ( w.getPosition().getEffectiveY() < 0 )
{
w.getPosition().setEffectivePosition( w.getPosition().getEffectiveX(), 0 );
}
if ( w.getPosition().getEffectiveX() + w.getSize().getEffectiveWidth() >= gameResX )
{
int newX = gameResX - w.getSize().getEffectiveWidth();
if ( newX < 0 )
{
w.getPosition().setEffectivePosition( 0, w.getPosition().getEffectiveY() );
w.getSize().setEffectiveSize( gameResX, w.getSize().getEffectiveHeight() );
}
else
{
w.getPosition().setEffectivePosition( newX, w.getPosition().getEffectiveY() );
}
}
if ( w.getPosition().getEffectiveY() + w.getSize().getEffectiveHeight() >= gameResY )
{
int newY = gameResY - w.getSize().getEffectiveHeight();
if ( newY < 0 )
{
w.getPosition().setEffectivePosition( w.getPosition().getEffectiveX(), 0 );
w.getSize().setEffectiveSize( w.getSize().getEffectiveWidth(), gameResY );
}
else
{
w.getPosition().setEffectivePosition( w.getPosition().getEffectiveX(), newY );
}
}
//w.getPosition().set( w.getPosition().getPositioning(), w.getPosition().getX(), w.getPosition().getY() );
//w.getSize().set( w.getSize().getWidth(), w.getSize().getHeight() );
if ( !isEditorMode )
{
w.bake( !isEditorMode );
}
}
needsCheckFixAndBake = false;
}
/*
* Gets the general store object for the given Widget class.
*
* @param widgetClass
*
* @return the general store object for the given Widget class.
*/
/*
final Object getGeneralStore( Class<? extends StatefulWidget> widgetClass )
{
Object generalStore = generalStores.get( widgetClass );
if ( generalStore == null )
{
try
{
StatefulWidget widget = (StatefulWidget)WidgetFactory.createWidget( widgetClass, "dummy" );
generalStore = widget.getGeneralStore();
generalStores.put( widgetClass, generalStore );
}
catch ( Throwable t )
{
Logger.log( t );
}
}
return ( generalStore );
}
*/
/**
* Maps a new named color.
*
* @param name the name
* @param color the color
*
* @return changed?
*/
public boolean addNamedColor( String name, Color color )
{
Color old = this.colorMap.put( name, color );
return ( !color.equals( old ) );
}
/**
* Gets a named color from the map or <code>null</code>, if not found.
*
* @param name the name
*
* @return a named color from the map or <code>null</code>, if not found.
*/
public final Color getNamedColor( String name )
{
return ( colorMap.get( name ) );
}
/**
* Gets all currently mapped color names.
*
* @return all currently mapped color names.
*/
public final Set<String> getColorNames()
{
return ( colorMap.keySet() );
}
/**
* Removes a mapped named color.
*
* @param name the name
*
* @return the previously mapped color.
*/
public Color removeNamedColor( String name )
{
Color color = colorMap.remove( name );
if ( color != null )
{
String colorString = ColorUtils.colorToHex( color );
resetColors( name, colorString );
}
return ( color );
}
private static void renameColorPropertyValues( List<Property> list, String oldName, String newName )
{
for ( Property prop : list )
{
if ( prop instanceof ColorProperty )
{
ColorProperty colorProp = (ColorProperty)prop;
if ( ( colorProp.getValue() != null ) && colorProp.getValue().equals( oldName ) )
colorProp.setValue( newName );
}
else if ( ( prop instanceof BackgroundProperty ) && ( (BackgroundProperty)prop ).getBackgroundType().isColor() )
{
ColorProperty colorProp = ( (BackgroundProperty)prop ).getColorProperty();
if ( ( colorProp.getValue() != null ) && colorProp.getValue().equals( oldName ) )
colorProp.setValue( newName );
}
}
}
/**
* Renames the color.
*
* @param oldName the old name
* @param newName the new name
*/
public void renameColor( String oldName, String newName )
{
Color color = colorMap.get( oldName );
if ( color == null )
return;
colorMap.remove( oldName );
colorMap.put( newName, color );
FlatPropertiesContainer propsCont = new FlatPropertiesContainer();
for ( Widget widget : widgets )
{
propsCont.clear();
widget.getProperties( propsCont, true );
renameColorPropertyValues( propsCont.getList(), oldName, newName );
}
}
/**
* Reset colors to defaults.
*
* @param oldName the old name
* @param newValue the new name
*/
public void resetColors( String oldName, String newValue )
{
Color color = colorMap.get( oldName );
if ( color == null )
return;
colorMap.remove( oldName );
FlatPropertiesContainer propsCont = new FlatPropertiesContainer();
for ( Widget widget : widgets )
{
propsCont.clear();
widget.getProperties( propsCont, true );
renameColorPropertyValues( propsCont.getList(), oldName, newValue );
}
}
/**
* Maps a new named font.
*
* @param name the name
* @param fontStr tje font definition
*
* @return changed?
*/
public boolean addNamedFont( String name, String fontStr )
{
Font font = FontUtils.parseFont( fontStr, gameResolution.getViewportHeight(), false, true );
boolean virtual = FontUtils.parseVirtualFlag( fontStr, false, true );
Font oldFont = this.fontMap.put( name, font );
this.fontStringMap.put( name, fontStr );
Boolean oldVirt = this.fontVirtualMap.put( name, virtual );
return ( !font.equals( oldFont ) || !oldVirt.equals( virtual ) );
}
/**
* Gets a named font from the map or <code>null</code>, if not found.
*
* @param name the name
*
* @return a named font from the map or <code>null</code>, if not found.
*/
public final Font getNamedFont( String name )
{
return ( fontMap.get( name ) );
}
/**
* Gets a named font from the map or <code>null</code>, if not found.
*
* @param name the name
*
* @return a named font from the map or <code>null</code>, if not found.
*/
public final String getNamedFontString( String name )
{
return ( fontStringMap.get( name ) );
}
/**
* Gets a named font's virtual flag from the map or <code>null</code>, if not found.
*
* @param name the name
*
* @return a named font's virtual flag from the map or <code>null</code>, if not found.
*/
public final Boolean getNamedFontVirtual( String name )
{
return ( fontVirtualMap.get( name ) );
}
/**
* Gets all currently mapped font names.
*
* @return all currently mapped font names.
*/
public final Set<String> getFontNames()
{
return ( fontMap.keySet() );
}
/**
* Removes a mapped named font.
*
* @param name the name
*
* @return the previously mapped font.
*/
public Font removeNamedFont( String name )
{
Font font = fontMap.remove( name );
String fontString = fontStringMap.remove( name );
Boolean virtual = fontVirtualMap.remove( name );
if ( font != null )
{
boolean antiAliased = FontUtils.parseAntiAliasFlag( fontString, false, true );
fontString = FontUtils.getFontString( font, virtual, antiAliased );
resetFonts( name, fontString );
}
return ( font );
}
private static void renameFontPropertyValues( List<Property> list, String oldName, String newName )
{
for ( Property prop : list )
{
if ( prop instanceof FontProperty )
{
FontProperty fontProp = (FontProperty)prop;
if ( ( fontProp.getValue() != null ) && fontProp.getValue().equals( oldName ) )
fontProp.setValue( newName );
}
}
}
private static void resetAllFontProperties( List<Property> list )
{
for ( Property prop : list )
{
if ( prop instanceof FontProperty )
{
FontProperty fontProp = (FontProperty)prop;
fontProp.setValue( fontProp.getValue() );
}
}
}
private void resetAllFontProperties()
{
FlatPropertiesContainer propsCont = new FlatPropertiesContainer();
for ( Widget widget : widgets )
{
propsCont.clear();
widget.getProperties( propsCont, true );
resetAllFontProperties( propsCont.getList() );
}
}
public void renameFont( String oldName, String newName )
{
Font font = fontMap.get( oldName );
if ( font == null )
return;
fontMap.remove( oldName );
fontMap.put( newName, font );
fontStringMap.put( newName, fontStringMap.remove( oldName ) );
fontVirtualMap.put( newName, fontVirtualMap.remove( oldName ) );
FlatPropertiesContainer propsCont = new FlatPropertiesContainer();
for ( Widget widget : widgets )
{
propsCont.clear();
widget.getProperties( propsCont, true );
renameFontPropertyValues( propsCont.getList(), oldName, newName );
//break;
}
}
public void resetFonts( String oldName, String newValue )
{
Font font = fontMap.get( oldName );
if ( font == null )
return;
fontMap.remove( oldName );
fontStringMap.remove( oldName );
fontVirtualMap.remove( oldName );
FlatPropertiesContainer propsCont = new FlatPropertiesContainer();
for ( Widget widget : widgets )
{
propsCont.clear();
widget.getProperties( propsCont, true );
renameFontPropertyValues( propsCont.getList(), oldName, newValue );
//break;
}
}
/**
* Maps a new border alias to its filename.
*
* @param alias the alias name
* @param border the border
*
* @return changed?
*/
public boolean addBorderAlias( String alias, String border )
{
String old = borderMap.put( alias, border );
return ( !border.equals( old ) );
}
/**
* Gets a border filename from the map or <code>null</code>, if not found.
*
* @param alias the alias name
*
* @return a border filename from the map or <code>null</code>, if not found.
*/
public final String getBorderName( String alias )
{
return ( borderMap.get( alias ) );
}
/**
* Gets all currently mapped font names.
*
* @return all currently mapped font names.
*/
public final Set<String> getBorderAliases()
{
return ( borderMap.keySet() );
}
/**
* Removes a mapped border alias.
*
* @param alias the alias name
*
* @return the previously mapped border alias.
*/
public String removeBorderAlias( String alias )
{
String border = borderMap.remove( alias );
if ( border != null )
{
resetFonts( alias, border );
}
return ( border );
}
private static void renameBorderPropertyValues( List<Property> list, String oldName, String newName )
{
for ( Property prop : list )
{
if ( prop instanceof BorderProperty )
{
BorderProperty borderProp = (BorderProperty)prop;
if ( ( borderProp.getValue() != null ) && borderProp.getValue().equals( oldName ) )
borderProp.setValue( newName );
}
}
}
public void renameBorder( String oldName, String newName )
{
String border = borderMap.get( oldName );
if ( border == null )
return;
borderMap.remove( oldName );
borderMap.put( newName, border );
FlatPropertiesContainer propsCont = new FlatPropertiesContainer();
for ( Widget widget : widgets )
{
propsCont.clear();
widget.getProperties( propsCont, true );
renameBorderPropertyValues( propsCont.getList(), oldName, newName );
//break;
}
}
public void resetBorders( String oldName, String newValue )
{
String border = borderMap.get( oldName );
if ( border == null )
return;
borderMap.remove( oldName );
FlatPropertiesContainer propsCont = new FlatPropertiesContainer();
for ( Widget widget : widgets )
{
propsCont.clear();
widget.getProperties( propsCont, true );
renameBorderPropertyValues( propsCont.getList(), oldName, newValue );
//break;
}
}
/**
* Gets whether class relative scoring is enabled or not.
*
* @return whether class relative scoring is enabled or not.
*/
public final boolean getUseClassScoring()
{
return ( useClassScoring.getBooleanValue() );
}
/**
* {@inheritDoc}
*/
@Override
public void onPropertyChanged( Property property, Object oldValue, Object newValue )
{
for ( int i = 0; i < getNumWidgets(); i++ )
getWidget( i ).forceAndSetDirty( true );
}
/**
* {@inheritDoc}
*/
@Override
public void saveProperties( PropertyWriter writer ) throws IOException
{
writer.writeProperty( useClassScoring, "Ignore vehicles from other classes than the viewed one for scoring?" );
}
/**
* {@inheritDoc}
*/
@Override
public void loadProperty( PropertyLoader loader )
{
if ( loader.loadProperty( useClassScoring ) );
}
/**
* {@inheritDoc}
*/
@Override
public void getProperties( PropertiesContainer propsCont, boolean forceAll )
{
//propsCont.addGroup( "General" );
propsCont.addProperty( useClassScoring );
}
/**
* Creates a new {@link WidgetsConfiguration}.
*
* @param gameResX
* @param gameResY
*/
public WidgetsConfiguration( int gameResX, int gameResY )
{
__GDPrivilegedAccess.setGameResolution( gameResX, gameResY, this );
__WCPrivilegedAccess.setViewport( 0, 0, gameResX, gameResY, this );
AbstractPropertiesKeeper.attachKeeper( this );
}
}