//$HeadURL$
/*---------------- FILE HEADER ------------------------------------------
This file is part of deegree.
Copyright (C) 2001-2012 by:
Department of Geography, University of Bonn
http://www.giub.uni-bonn.de/deegree/
lat/lon GmbH
http://www.lat-lon.de
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Contact:
lat/lon GmbH
Aennchenstr. 19
53177 Bonn
Germany
E-Mail: info@lat-lon.de
Prof. Dr. Klaus Greve
Department of Geography
University of Bonn
Meckenheimer Allee 166
53115 Bonn
Germany
E-Mail: greve@giub.uni-bonn.de
---------------------------------------------------------------------------*/
package org.deegree.igeo.mapmodel;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.deegree.crs.configuration.CRSConfiguration;
import org.deegree.crs.configuration.CRSProvider;
import org.deegree.framework.log.ILogger;
import org.deegree.framework.log.LoggerFactory;
import org.deegree.framework.util.MapUtils;
import org.deegree.graphics.transformation.GeoTransform;
import org.deegree.graphics.transformation.WorldToScreenTransform;
import org.deegree.igeo.ApplicationContainer;
import org.deegree.igeo.ChangeListener;
import org.deegree.igeo.ValueChangedEvent;
import org.deegree.igeo.config.CRSType;
import org.deegree.igeo.config.EnvelopeType;
import org.deegree.igeo.config.ExternalResourceType;
import org.deegree.igeo.config.MapModelType;
import org.deegree.igeo.config.TargetDeviceType;
import org.deegree.igeo.config.Util;
import org.deegree.igeo.dataadapter.DataAccessAdapter;
import org.deegree.igeo.i18n.Messages;
import org.deegree.igeo.mapmodel.MapModelChangedEvent.CHANGE_TYPE;
import org.deegree.igeo.modules.ModuleCreator;
import org.deegree.model.Identifier;
import org.deegree.model.crs.CoordinateSystem;
import org.deegree.model.spatialschema.Envelope;
import org.deegree.model.spatialschema.GeometryFactory;
/**
* Base class for modeling a map. A map model may contains n {@link LayerGroup}s that may contain n {@link Layer}s and m
* {@link LayerGroup}s; so map model is organized as a tree.<br>
* To access each node of this tree the method {@link #walkLayerTree(MapModelVisitor)} is provided that walks the
* complete tree. For each layer and layer group contained in model a special method of the passed visitor will be
* invoked.<br>
* Each {@link MapModelEntry} that is part of a {@link MapModel} can be selected for a specific action. Any kind of
* changes provided to a {@link MapModel} including selection state of the {@link MapModelEntry}s will fire an event
* that will be provided to all registered listeners.
*
*
* @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
* @author last edited by: $Author$
*
* @version. $Revision$, $Date$
*/
public class MapModel implements ChangeListener {
public static final String SELECTION_EDITING = "editing";
public static final String SELECTION_ACTION = "action";
public static final ILogger LOG = LoggerFactory.getLogger( MapModel.class );
transient protected List<ChangeListener> listeners = new ArrayList<ChangeListener>();
private MapModelType mmType;
private List<LayerGroup> layerGroups;
private ApplicationContainer<?> appContainer;
/**
* @param appContainer
* @param mmType
* @param LayerGroups
* @param description
*/
public MapModel( ApplicationContainer<?> appContainer, MapModelType mmType ) {
this.mmType = mmType;
this.layerGroups = new ArrayList<LayerGroup>();
this.appContainer = appContainer;
}
/**
* @return the identifier
*/
public Identifier getIdentifier() {
return Util.convertIdentifier( mmType.getIdentifier() );
}
@Override
public boolean equals( Object obj ) {
if ( obj == null || !( obj instanceof MapModel ) ) {
return false;
}
return getIdentifier().equals( ( (MapModel) obj ).getIdentifier() );
}
@Override
public int hashCode() {
return getIdentifier().hashCode();
}
/**
* @return the name
*/
public String getName() {
return mmType.getName();
}
/**
*
* @return <code>true</code> if a {@link MapModel} is the active/current one
*/
public boolean isCurrent() {
return mmType.isCurrent();
}
/**
*
* @param current
* true if a {@link MapModel} shall be marked as active/current one
*/
public void setCurrent( boolean current ) {
mmType.setCurrent( current );
}
/**
*
* @return application container which is owner of a {@link ModuleCreator} instance
*/
public ApplicationContainer<?> getApplicationContainer() {
return appContainer;
}
/**
*
* @return description
*/
public String getDescription() {
return mmType.getDescription();
}
/**
*
* @param description
* map model description
*/
public void setDescription( String description ) {
mmType.setDescription( description );
}
/**
*
* @return current selected extent
*/
public Envelope getEnvelope() {
return Util.convertEnvelope( mmType.getExtent() );
}
/**
*
* @return map models CRS
*/
public CoordinateSystem getCoordinateSystem() {
return Util.convertEnvelope( mmType.getExtent() ).getCoordinateSystem();
}
/**
*
* @param visitor
* @throws Exception
*/
public void walkLayerTree( MapModelVisitor visitor )
throws Exception {
for ( LayerGroup layerGroup : layerGroups ) {
applyVisitor( layerGroup, visitor );
}
}
private void applyVisitor( LayerGroup layerGroup, MapModelVisitor visitor )
throws Exception {
visitor.visit( layerGroup );
List<MapModelEntry> entries = layerGroup.getMapModelEntries();
for ( MapModelEntry entry : entries ) {
if ( entry instanceof Layer ) {
visitor.visit( (Layer) entry );
} else {
applyVisitor( (LayerGroup) entry, visitor );
}
}
}
/**
*
* @param envelope
*/
public void setEnvelope( Envelope envelope ) {
if ( !envelope.equals( getEnvelope() ) ) {
EnvelopeType value = new EnvelopeType();
CoordinateSystem crs = envelope.getCoordinateSystem();
if ( crs == null ) {
crs = getCoordinateSystem();
}
value.setCrs( crs.getPrefixedName() );
value.setMinx( envelope.getMin().getX() );
value.setMiny( envelope.getMin().getY() );
value.setMaxx( envelope.getMax().getX() );
value.setMaxy( envelope.getMax().getY() );
mmType.setExtent( value );
fireMapModelChangedEvent( CHANGE_TYPE.extentChanged, envelope );
}
}
public void repaint()
throws Exception {
walkLayerTree( new MapModelVisitor() {
@Override
public void visit( Layer layer )
throws Exception {
for ( DataAccessAdapter daa : layer.getDataAccess() ) {
daa.refresh( true );
}
}
@Override
public void visit( LayerGroup layerGroup )
throws Exception {
}
} );
fireMapModelChangedEvent( CHANGE_TYPE.repaintForced, null );
}
/**
*
* @return list of assigned external resources/documents
*/
public List<ExternalResourceType> getExternalResources() {
return Collections.unmodifiableList( mmType.getExternalResource() );
}
/**
*
* @param externalResources
*/
public void setExternalResources( List<ExternalResourceType> externalResources ) {
List<ExternalResourceType> tmp = mmType.getExternalResource();
tmp.clear();
tmp.addAll( externalResources );
}
/**
*
* @param externalResource
*/
public void addExternalResources( ExternalResourceType externalResource ) {
List<ExternalResourceType> tmp = mmType.getExternalResource();
tmp.add( externalResource );
}
/**
*
* @param externalResource
*/
public void removeExternalResources( ExternalResourceType externalResource ) {
List<ExternalResourceType> tmp = mmType.getExternalResource();
tmp.remove( externalResource );
}
/**
*
* @param identifier
* @return {@link MapModelEntry} matching the passed {@link Identifier} or null if no {@link MapModelEntry} is
* matching
*/
public MapModelEntry getMapModelEntry( final Identifier identifier ) {
final MapModelEntry[] mme = new MapModelEntry[1];
try {
walkLayerTree( new MapModelVisitor() {
public void visit( Layer layer )
throws Exception {
if ( layer.getIdentifier().equals( identifier ) ) {
mme[0] = layer;
}
}
public void visit( LayerGroup layerGroup )
throws Exception {
if ( layerGroup.getIdentifier().equals( identifier ) ) {
mme[0] = layerGroup;
}
}
} );
} catch ( Exception e ) {
// should never happen
LOG.logWarning( "", e );
}
return mme[0];
}
/**
*
* @param layer
* layer to remove
*/
public void remove( MapModelEntry mapModelEntry ) {
if ( mapModelEntry instanceof Layer ) {
( (Layer) mapModelEntry ).getParent().removeLayer( (Layer) mapModelEntry );
fireMapModelChangedEvent( CHANGE_TYPE.layerRemoved, mapModelEntry );
} else if ( mapModelEntry instanceof LayerGroup ) {
LayerGroup layerGroup = (LayerGroup) mapModelEntry;
if ( layerGroup.getParent() != null ) {
layerGroup.getParent().removeLayerGroup( layerGroup );
} else {
mmType.getLayerGroup().remove( layerGroup.getLayerGroupType() );
}
fireMapModelChangedEvent( CHANGE_TYPE.layerGroupRemoved, layerGroup );
}
}
/**
*
* @param mapModelEntry
* {@link MapModelEntry} to be inserted
* @param parent
* if <code>null</code> root node of layertree will be used as parent
* @param antecessor
* if <code>null</code> layer will be inserted directly underneath its parent
* @param first
* if true layer will be inserted as first layer of a group if antecessor == null
*/
public void insert( final MapModelEntry mapModelEntry, LayerGroup parent, MapModelEntry antecessor, boolean first ) {
if ( exists( mapModelEntry.getIdentifier() ) ) {
throw new MapModelException( Messages.get( "$DG10087", mapModelEntry.getIdentifier() ) );
}
if ( mapModelEntry instanceof Layer ) {
insertLayer( (Layer) mapModelEntry, parent, antecessor, first );
} else if ( mapModelEntry instanceof LayerGroup ) {
insertLayerGroup( (LayerGroup) mapModelEntry, parent, antecessor, first );
}
}
/**
*
* @param identifier
* @return <code>true</code> if layer with passed {@link Identifier} exists
*/
public boolean exists( final Identifier identifier ) {
try {
// check if layer already exists in map model
walkLayerTree( new MapModelVisitor() {
public void visit( Layer layer )
throws Exception {
if ( layer.getIdentifier().equals( identifier ) ) {
throw new MapModelException( Messages.get( "$DG10087", identifier ) );
}
}
public void visit( LayerGroup layerGroup )
throws Exception {
if ( layerGroup.getIdentifier().equals( identifier ) ) {
throw new MapModelException( Messages.get( "$DG10088", identifier ) );
}
}
} );
} catch ( Exception e ) {
if ( e instanceof MapModelException ) {
return true;
} else {
throw new MapModelException( e.getMessage(), e );
}
}
return false;
}
class GetLayerByIdentifierMapModelVisitor implements MapModelVisitor {
Identifier identifier;
public GetLayerByIdentifierMapModelVisitor( Identifier identifier ) {
this.identifier = identifier;
}
private Layer layer;
public void visit( Layer layer )
throws Exception {
if ( layer.getIdentifier().equals( identifier ) ) {
this.layer = layer;
}
}
public void visit( LayerGroup layerGroup )
throws Exception {
}
public Layer getLayer() {
return layer;
}
}
/**
*
* @param identifier
* @return layer matching passed {@link Identifier}
*/
public Layer getLayerByIdentifier( final Identifier identifier ) {
GetLayerByIdentifierMapModelVisitor v = new GetLayerByIdentifierMapModelVisitor( identifier );
try {
walkLayerTree( v );
return v.getLayer();
} catch ( Exception e ) {
throw new MapModelException( e.getMessage(), e );
}
}
/**
*
* @param layer
* @param parent
* if <code>null</code> root node of layertree will be used as parent
* @param antecessor
* if <code>null</code> layer will be inserted directly underneath its parent
* @param first
* if true layer will be inserted as first layer of a group if antecessor == null
*/
private void insertLayer( Layer layer, LayerGroup parent, MapModelEntry antecessor, boolean first ) {
insertLayer( layer, parent, antecessor, layerGroups, first );
fireMapModelChangedEvent( CHANGE_TYPE.layerInserted, layer );
}
private void insertLayer( Layer layer, LayerGroup parent, MapModelEntry antecessor, List<LayerGroup> lgs,
boolean first ) {
for ( LayerGroup layerGroup : lgs ) {
if ( parent != null && parent.equals( layerGroup ) ) {
layerGroup.insert( layer, antecessor, first );
break;
} else {
insertLayer( layer, parent, antecessor, layerGroup.getLayerGroups(), first );
}
}
}
/**
*
* @param layerGroup
* @param parent
* if <code>null</code> root node of layertree will be used as parent
* @param antecessor
* if <code>null</code> layer will be inserted directly underneath its parent
* @param first
* if true layer will be inserted as first layergroup of a group if antecessor == null
*/
private void insertLayerGroup( LayerGroup layerGroup, LayerGroup parent, MapModelEntry antecessor, boolean first ) {
if ( parent == null ) {
layerGroups.add( layerGroup );
}
insertLayerGroup( layerGroup, parent, antecessor, layerGroups, first );
fireMapModelChangedEvent( CHANGE_TYPE.layerGroupInserted, layerGroup );
}
private void insertLayerGroup( LayerGroup lg, LayerGroup parent, MapModelEntry antecessor, List<LayerGroup> lgs,
boolean first ) {
for ( LayerGroup layerGroup : lgs ) {
if ( parent != null && parent.equals( layerGroup ) ) {
layerGroup.insert( lg, antecessor, first );
break;
} else {
insertLayerGroup( lg, parent, antecessor, layerGroup.getLayerGroups(), first );
}
}
}
/**
* moves the passed layer underneath a new parent and before the passed antecessor.
*
* @param layer
* @param parent
* if <code>null</code> root node of layertree will be used as parent
* @param antecessor
* if <code>null</code> layer will be inserted directly underneath its parent
*/
public void move( MapModelEntry mapModelEntry, LayerGroup parent, MapModelEntry antecessor, boolean first ) {
if ( mapModelEntry instanceof Layer ) {
move( (Layer) mapModelEntry, parent, antecessor, first );
} else if ( mapModelEntry instanceof LayerGroup ) {
move( (LayerGroup) mapModelEntry, parent, antecessor, first );
}
if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
printModel( System.out );
}
}
/**
* moves the passed layer underneath a new parent and before the passed antecessor.
*
* @param layer
* @param parent
* if <code>null</code> root node of layertree will be used as parent
* @param antecessor
* if <code>null</code> layer will be inserted directly underneath its parent
* @param first
* if true layer will be inserted as first layer of a group if antecessor == null
*/
private void move( Layer layer, LayerGroup parent, MapModelEntry antecessor, boolean first ) {
if ( !layer.equals( antecessor ) ) {
layer.getParent().removeLayer( layer );
insertLayer( layer, parent, antecessor, layerGroups, first );
fireMapModelChangedEvent( CHANGE_TYPE.layerOrderChanged, layer );
}
}
/**
* moves the passed layergroup underneath a new parent and before the passed antecessor.
*
* @param layerGroup
* @param parent
* if <code>null</code> root node of layertree will be used as parent
* @param antecessor
* if <code>null</code> layergroup will be inserted directly underneath its parent
*/
private void move( LayerGroup layerGroup, LayerGroup parent, MapModelEntry antecessor, boolean first ) {
if ( !layerGroup.equals( antecessor ) ) {
layerGroup.getParent().removeLayerGroup( layerGroup );
insertLayerGroup( layerGroup, parent, antecessor, layerGroups, first );
fireMapModelChangedEvent( CHANGE_TYPE.layerOrderChanged, layerGroup );
}
}
/**
*
* @return maximum extent of a MapModel
*/
public Envelope getMaxExtent() {
return Util.convertEnvelope( mmType.getMaxExtent() );
}
/**
*
* @param maxExtent
*/
public void setMaxExtent( Envelope envelope ) {
EnvelopeType value = new EnvelopeType();
value.setCrs( envelope.getCoordinateSystem().getPrefixedName() );
value.setMinx( envelope.getMin().getX() );
value.setMiny( envelope.getMin().getY() );
value.setMaxx( envelope.getMax().getX() );
value.setMaxy( envelope.getMax().getY() );
mmType.setMaxExtent( value );
}
/**
* @return the targetDevice
*/
public TargetDeviceType getTargetDevice() {
return mmType.getTargetDevice();
}
/**
*
* @return transformation object for transforming geo coordinates to target device coordinates and vice versa
*/
public GeoTransform getToTargetDeviceTransformation() {
int w = mmType.getTargetDevice().getPixelWidth();
int h = mmType.getTargetDevice().getPixelHeight();
Envelope target = GeometryFactory.createEnvelope( 0, 0, w - 1, h - 1, null );
return new WorldToScreenTransform( getEnvelope(), target );
}
/**
*
* @param action
* @return list of {@link MapModelEntry} selected for the passed action; including {@link Layer}s and
* {@link LayerGroup}s
*/
public List<MapModelEntry> getMapModelEntriesSelectedForAction( String action ) {
List<MapModelEntry> tmp = new ArrayList<MapModelEntry>();
getMapModelEntriesSelectedForAction( layerGroups, action, tmp );
return Collections.unmodifiableList( tmp );
}
private void getMapModelEntriesSelectedForAction( List<LayerGroup> lgs, String action, List<MapModelEntry> collector ) {
for ( LayerGroup layerGroup : lgs ) {
if ( layerGroup.getSelectedFor().contains( action ) ) {
collector.add( layerGroup );
}
List<MapModelEntry> mapModelEntries = layerGroup.getMapModelEntries();
for ( MapModelEntry mapModelEntrry : mapModelEntries ) {
if ( mapModelEntrry.getSelectedFor().contains( action ) ) {
collector.add( mapModelEntrry );
}
}
getMapModelEntriesSelectedForAction( layerGroup.getLayerGroups(), action, collector );
}
}
/**
*
* @param action
* @return list of {@link LayerGroup} selected for the passed action
*/
public List<LayerGroup> getLayerGroupsSelectedForAction( String action ) {
List<LayerGroup> tmp = new ArrayList<LayerGroup>();
getLayerGroupsForAction( layerGroups, action, tmp );
return Collections.unmodifiableList( tmp );
}
private void getLayerGroupsForAction( List<LayerGroup> lgs, String action, List<LayerGroup> collector ) {
for ( LayerGroup layerGroup : lgs ) {
List<LayerGroup> mapModelEntries = layerGroup.getLayerGroups();
for ( LayerGroup mapModelEntrry : mapModelEntries ) {
if ( mapModelEntrry.getSelectedFor().contains( action ) ) {
collector.add( mapModelEntrry );
}
}
getLayerGroupsForAction( layerGroup.getLayerGroups(), action, collector );
}
}
/**
*
* @param action
* @return list of {@link Layer} selected for the passed action
*/
public List<Layer> getLayersSelectedForAction( String action ) {
List<Layer> tmp = new ArrayList<Layer>();
getLayersForAction( layerGroups, action, tmp );
return Collections.unmodifiableList( tmp );
}
private void getLayersForAction( List<LayerGroup> lgs, String action, List<Layer> collector ) {
for ( LayerGroup layerGroup : lgs ) {
List<Layer> mapModelEntries = layerGroup.getLayers();
for ( Layer mapModelEntrry : mapModelEntries ) {
if ( mapModelEntrry.getSelectedFor().contains( action ) ) {
collector.add( mapModelEntrry );
}
}
getLayersForAction( layerGroup.getLayerGroups(), action, collector );
}
}
private void fireMapModelChangedEvent( CHANGE_TYPE changeType, Object value ) {
MapModelChangedEvent event = new MapModelChangedEvent( changeType, this, value, null );
for ( int i = 0; i < this.listeners.size(); i++ ) {
listeners.get( i ).valueChanged( event );
}
}
/**
* @param listener
* to be invoked if a {@link MapModel} has been changed
*/
public void addChangeListener( ChangeListener listener ) {
this.listeners.add( listener );
}
/**
* @param listener
* to be removed
*/
public void removeChangeListener( ChangeListener listener ) {
this.listeners.remove( listener );
}
/**
*
* @return list of registered change listeners
*/
public List<ChangeListener> getChangeListener() {
return this.listeners;
}
/**
* @param event
*/
public void valueChanged( ValueChangedEvent event ) {
if ( event instanceof LayerChangedEvent ) {
event = new MapModelChangedEvent( CHANGE_TYPE.layerStateChanged, this, event.getValue(), event );
for ( int i = 0; i < this.listeners.size(); i++ ) {
listeners.get( i ).valueChanged( event );
}
} else if ( event instanceof MapModelChangedEvent ) {
for ( int i = 0; i < this.listeners.size(); i++ ) {
listeners.get( i ).valueChanged( event );
}
}
}
/**
*
* @param layerGroups
*/
public void setLayerGroups( List<LayerGroup> layerGroups ) {
this.layerGroups = layerGroups;
}
/**
* @return the layerGroups
*/
public List<LayerGroup> getLayerGroups() {
return layerGroups;
}
/**
*
* @param isVisible
* if set to true just layers set to be visible will be returned
* @return all layers as a list
*/
public List<Layer> getLayersAsList( final boolean isVisible ) {
final List<Layer> list = new ArrayList<Layer>( 50 );
try {
walkLayerTree( new MapModelVisitor() {
public void visit( LayerGroup layerGroup )
throws Exception {
// do nothing
}
public void visit( Layer layer )
throws Exception {
if ( ( isVisible && layer.isVisible() ) || !isVisible ) {
list.add( layer );
}
}
} );
} catch ( Exception e ) {
// ignore
LOG.logWarning( "", e );
}
return list;
}
/**
*
* @return current scale denominator of a map model
*/
public double getScaleDenominator() {
Envelope env = getEnvelope();
int h = getTargetDevice().getPixelHeight();
int w = getTargetDevice().getPixelWidth();
return MapUtils.calcScale( w, h, env, getCoordinateSystem(), MapUtils.DEFAULT_PIXEL_SIZE );
}
/**
*
* @param supportedCRSs
* @return array of supported CRS
*/
public CRSEntry[] getSupportedCRSs() {
CRSEntry[] entries;
if ( mmType.getSupportedCRS() != null ) {
List<CRSType> supportedCRSs = mmType.getSupportedCRS().getCRS();
if ( supportedCRSs.size() > 0 ) {
entries = new CRSEntry[supportedCRSs.size()];
// user defined list of supported CRS is available
for ( int i = 0; i < supportedCRSs.size(); i++ ) {
CRSType crs = supportedCRSs.get( i );
entries[i] = new CRSEntry( crs.getCode(), crs.getName() );
}
} else {
// use all supported CRSs
CRSProvider pr = CRSConfiguration.getCRSConfiguration().getProvider();
List<String> tmp = pr.getAvailableCRSIds();
List<CRSEntry> tmp2 = new ArrayList<CRSEntry>( tmp.size() / 2 );
for ( String string : tmp ) {
if ( string.toLowerCase().startsWith( "epsg:" ) ) {
tmp2.add( new CRSEntry( string, string ) );
}
}
Collections.sort( tmp2 );
entries = tmp2.toArray( new CRSEntry[tmp2.size()] );
}
} else {
entries = new CRSEntry[0];
}
return entries;
}
/**
* prints this mapmodel to passed stream
*
* @param os
*/
public void printModel( final PrintStream os ) {
os.println( "=========== Map Model ==========================" );
try {
walkLayerTree( new MapModelVisitor() {
public void visit( LayerGroup layerGroup )
throws Exception {
String s = layerGroup.getTitle();
LayerGroup lg = layerGroup.getParent();
while ( lg != null ) {
s = lg.getTitle() + "." + s;
lg = lg.getParent();
}
os.println( "LG: " + s );
}
public void visit( Layer layer )
throws Exception {
String s = layer.getTitle();
LayerGroup lg = layer.getParent();
while ( lg != null ) {
s = lg.getTitle() + "." + s;
lg = lg.getParent();
}
os.println( "Layer: " + s );
}
} );
} catch ( Exception e ) {
e.printStackTrace();
}
}
@Override
public String toString() {
return getName();
}
}