/*
* Bibliothek - DockingFrames
* Library built on Java/Swing, allows the user to "drag and drop"
* panels containing any Swing-Component the developer likes to add.
*
* Copyright (C) 2007 Benjamin Sigg
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Benjamin Sigg
* benjamin_sigg@gmx.ch
* CH - Switzerland
*/
package bibliothek.gui.dock.common.intern;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import bibliothek.gui.dock.common.CContentArea;
import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CControlRegister;
import bibliothek.gui.dock.common.CStation;
import bibliothek.gui.dock.common.CStationContainer;
import bibliothek.gui.dock.common.CStationContainerListener;
import bibliothek.gui.dock.common.MultipleCDockable;
import bibliothek.gui.dock.common.MultipleCDockableFactory;
import bibliothek.gui.dock.common.SingleCDockable;
import bibliothek.gui.dock.common.SingleCDockableFactory;
import bibliothek.gui.dock.common.mode.ExtendedMode;
import bibliothek.gui.dock.frontend.FrontendEntry;
/**
* Standard implementation of {@link CControlRegister}.
* @author Benjamin Sigg
*/
public class DefaultCControlRegister implements MutableCControlRegister {
/** the owner of this register */
private CControl control;
/** the center component of the main-frame */
private CContentArea content;
/** whether the default stations are currently added */
private boolean settingDefaultStations = false;
/** the whole list of station-containers known to this control, includes {@link #content} */
private List<CStationContainer> containers = new ArrayList<CStationContainer>();
/** A factory used to create missing {@link SingleCDockable}s */
private CommonSingleDockableFactory backupFactory;
/** the set of known factories */
private Map<String, CommonMultipleDockableFactory> factories =
new HashMap<String, CommonMultipleDockableFactory>();
/** list of all dockables registered */
private List<CDockable> dockables =
new ArrayList<CDockable>();
/** list of all {@link SingleCDockable}s */
private List<SingleCDockable> singleDockables =
new ArrayList<SingleCDockable>();
/** the set of {@link MultipleCDockable}s */
private List<MultipleCDockable> multiDockables =
new ArrayList<MultipleCDockable>();
/** the stations known */
private List<CStation<?>> stations = new ArrayList<CStation<?>>();
/** a listener added to all {@link CStationContainer}s, adds or removes {@link CStation}s
* when necessary */
private CStationContainerListener containerListener = new CStationContainerListener(){
public void removed( CStationContainer source, CStation<?> station ){
control.removeStation( station );
}
public void added( CStationContainer source, CStation<?> station ){
try{
settingDefaultStations = source == content;
control.addStation( station, true );
}
finally{
settingDefaultStations = false;
}
}
};
/**
* Creates a new register
* @param control the owner of this register
*/
public DefaultCControlRegister( CControl control ){
this.control = control;
backupFactory = new CommonSingleDockableFactory( control );
}
public CControl getControl() {
return control;
}
public List<CDockable> getDockables() {
return Collections.unmodifiableList( dockables );
}
public List<MultipleCDockable> getMultipleDockables() {
return Collections.unmodifiableList( multiDockables );
}
public List<SingleCDockable> getSingleDockables() {
return Collections.unmodifiableList( singleDockables );
}
public List<CStation<?>> getStations() {
return Collections.unmodifiableList( stations );
}
public List<CStationContainer> getStationContainers() {
return Collections.unmodifiableList( containers );
}
public CStationContainer getContainer( CStation<?> child ){
for( CStationContainer container : getStationContainers() ){
for( int i = 0, n = container.getStationCount(); i<n; i++ ){
if( container.getStation( i ) == child ){
return container;
}
}
}
return null;
}
public void addStationContainer( CStationContainer container ){
if( container == null )
throw new NullPointerException( "container is null" );
if( containers.contains( container ))
throw new IllegalArgumentException( "container already registered" );
for( CStationContainer child : containers ){
if( child.getUniqueId().equals( container.getUniqueId() )){
throw new IllegalArgumentException( "A container with unique id '" + container.getUniqueId() + "' is already registered" );
}
}
containers.add( container );
try{
settingDefaultStations = container == content;
for( int i = 0, n = container.getStationCount(); i<n; i++ ){
control.addStation( container.getStation( i ), true );
}
}
finally{
settingDefaultStations = false;
}
container.addStationContainerListener( containerListener );
}
/**
* Gets a list of keys for all {@link SingleCDockableFactory}s which
* are currently registered at this control.
* @return the list of keys
*/
public Set<String> listSingleBackupFactories(){
return backupFactory.getFactoryIds();
}
/**
* Gets a list of identifiers of all {@link MultipleCDockableFactory}s
* which are currently registered at this control.
* @return the list of factories
*/
public Set<String> listMultipleDockableFactories(){
return factories.keySet();
}
/**
* Gets a list of all {@link MultipleCDockable}s that are registered at this
* control and whose {@link MultipleCDockable#getFactory()} method returns
* <code>factory</code>.
* @param factory the factory to look out for
* @return the list of dockables, never <code>null</code> but might be empty
*/
public List<MultipleCDockable> listMultipleDockables( MultipleCDockableFactory<?, ?> factory ){
List<MultipleCDockable> result = new ArrayList<MultipleCDockable>();
for( MultipleCDockable dockable : multiDockables ){
if( dockable.getFactory() == factory ){
result.add( dockable );
}
}
return result;
}
/**
* Gets a list of all visible {@link CDockable}s in the given mode.
* @param mode the mode which each <code>CDockable</code> must have
* @return the list of <code>CDockable</code>s
*/
public List<CDockable> listDockablesInMode( ExtendedMode mode ){
List<CDockable> list = new ArrayList<CDockable>();
for( CDockable check : dockables ){
if( check.isVisible() && check.getExtendedMode() == mode ){
list.add( check );
}
}
return list;
}
/**
* Gets a list of all identifiers of {@link SingleCDockable} for which
* this control has location information within the current {@link CControl#load(String) setting}.
* @return the list of ids, never <code>null</code>
*/
public Set<String> listSingleDockables(){
Set<String> result = new HashSet<String>();
for( FrontendEntry entry : control.intern().listFrontendEntries() ){
String id = entry.getKey();
if( isSingleId( id )){
result.add( singleToNormalId( id ));
}
}
return result;
}
public String toSingleId( String id ){
return "single " + id;
}
public boolean isSingleId( String id ){
return id.startsWith( "single " );
}
public String singleToNormalId( String id ){
return id.substring( 7 );
}
public String toMultiId( String id ){
return "multi " + id;
}
public boolean isMultiId( String id ){
return id.startsWith( "multi " );
}
public String multiToNormalId( String id ){
return id.substring( 6 );
}
public void addMultipleDockable( MultipleCDockable dockable ) {
dockables.add( dockable );
multiDockables.add( dockable );
}
public void addSingleDockable( SingleCDockable dockable ) {
dockables.add( dockable );
singleDockables.add( dockable );
}
public SingleCDockable getSingleDockable( String id ){
for( SingleCDockable dockable : singleDockables ){
if( dockable.getUniqueId().equals( id )){
return dockable;
}
}
return null;
}
public void addStation( CStation<?> station ) {
if( !settingDefaultStations ){
checkStationIdentifierUniqueness( station.getUniqueId(), station );
}
stations.add( station );
}
/**
* Ensures the uniqueness of the identifier <code>uniqueId</code>. Throws
* various exceptions if the id is not unique.
* @param uniqueId the id that might be unique
* @param adding the station that is about to be added
*/
private void checkStationIdentifierUniqueness( String uniqueId, CStation<?> adding ){
if( uniqueId == null )
throw new NullPointerException( "uniqueId must not be null" );
if( CContentArea.getCenterIdentifier( CControl.CONTENT_AREA_STATIONS_ID ).equals( uniqueId ) )
throw new IllegalArgumentException( "The id " + uniqueId + " is reserved for special purposes" );
if( CContentArea.getEastIdentifier( CControl.CONTENT_AREA_STATIONS_ID ).equals( uniqueId ) )
throw new IllegalArgumentException( "The id " + uniqueId + " is reserved for special purposes" );
if( CContentArea.getWestIdentifier( CControl.CONTENT_AREA_STATIONS_ID ).equals( uniqueId ) )
throw new IllegalArgumentException( "The id " + uniqueId + " is reserved for special purposes" );
if( CContentArea.getSouthIdentifier( CControl.CONTENT_AREA_STATIONS_ID ).equals( uniqueId ) )
throw new IllegalArgumentException( "The id " + uniqueId + " is reserved for special purposes" );
if( CContentArea.getNorthIdentifier( CControl.CONTENT_AREA_STATIONS_ID ).equals( uniqueId ) )
throw new IllegalArgumentException( "The id " + uniqueId + " is reserved for special purposes" );
for( CStation<?> station : stations ){
if( station.getUniqueId().equals( uniqueId )){
if( station == adding ){
throw new IllegalArgumentException( "The station has already been registered" );
}
throw new IllegalArgumentException( "There exists already a station with id: " + uniqueId );
}
}
}
public CommonSingleDockableFactory getBackupFactory() {
return backupFactory;
}
public CommonMultipleDockableFactory getCommonMultipleDockableFactory( String id ) {
return factories.get( id );
}
public CContentArea getDefaultContentArea(){
return content;
}
public void putCommonMultipleDockableFactory( String id, CommonMultipleDockableFactory factory ) {
factories.put( id, factory );
}
public CommonMultipleDockableFactory removeCommonMultipleDockableFactory( String id ) {
return factories.remove( id );
}
public boolean removeStationContainer( CStationContainer container ){
if( containers.remove( container ) ){
container.removeStationContainerListener( containerListener );
for( int i = 0, n = container.getStationCount(); i<n; i++ ){
control.removeStation( container.getStation( i ) );
}
return true;
}
return false;
}
public boolean removeMultipleDockable( MultipleCDockable dockable ) {
if( dockables.remove( dockable ) ){
multiDockables.remove( dockable );
return true;
}
return false;
}
public boolean removeSingleDockable( SingleCDockable dockable ) {
if( dockables.remove( dockable )){
singleDockables.remove( dockable );
return true;
}
return false;
}
public boolean removeStation( CStation<?> station ) {
return stations.remove( station );
}
public void setDefaultContentArea( CContentArea container ){
this.content = container;
}
public CDockable getDockable( int index ) {
return dockables.get( index );
}
public int getDockableCount() {
return dockables.size();
}
public Map<String, MultipleCDockableFactory<?, ?>> getFactories() {
Map<String, MultipleCDockableFactory<?, ?>> result = new HashMap<String, MultipleCDockableFactory<?,?>>();
for( Map.Entry<String, CommonMultipleDockableFactory> entry : factories.entrySet() ){
result.put( entry.getKey(), entry.getValue().getFactory() );
}
return result;
}
public MultipleCDockableFactory<?, ?> getFactory( String id ) {
CommonMultipleDockableFactory factory = factories.get( id );
if( factory == null )
return null;
return factory.getFactory();
}
}