/*
* 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.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.DockFactory;
import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CStation;
import bibliothek.gui.dock.common.SingleCDockable;
import bibliothek.gui.dock.common.SingleCDockableFactory;
import bibliothek.gui.dock.common.perspective.CPerspective;
import bibliothek.gui.dock.common.perspective.CommonElementPerspective;
import bibliothek.gui.dock.common.perspective.SingleCDockablePerspective;
import bibliothek.gui.dock.layout.LocationEstimationMap;
import bibliothek.gui.dock.perspective.PerspectiveDockable;
import bibliothek.gui.dock.station.support.PlaceholderStrategy;
import bibliothek.util.Filter;
import bibliothek.util.Version;
import bibliothek.util.filter.PresetFilter;
import bibliothek.util.xml.XElement;
/**
* This factory is used to create new {@link SingleCDockable}s using various
* {@link SingleCDockableFactory}s. This factory is accessed when a single
* dockable is missing in the cache of its owning {@link CControl}.
* @author Benjamin Sigg
*/
public class CommonSingleDockableFactory implements DockFactory<CommonDockable, CommonElementPerspective, CommonSingleDockableLayout>{
public static final String BACKUP_FACTORY_ID = "ccontrol backup factory id";
/** all the factories that are used */
private List<Entry> factories = new ArrayList<Entry>();
/** factories using one id only */
private Map<String, Entry> singleIdFactories = new HashMap<String, Entry>();
private CControl control;
private CPerspective perspective;
/**
* Creates a new factory.
* @param control the owner of the factory, the factory will add {@link SingleCDockable}
* to this control
*/
public CommonSingleDockableFactory( CControl control ){
this.control = control;
}
/**
* Creates a new factory.
* @param control the owner of the factory, the factory will add {@link SingleCDockable}
* to this control
* @param perspective the perspective which is used to load perspective related content
*/
public CommonSingleDockableFactory( CControl control, CPerspective perspective ){
this.control = control;
this.perspective = perspective;
}
/**
* Registers a new factory that will load {@link SingleCDockable}s with
* the unique id <code>id</code>.
* @param id the unique id of the dockables which <code>factory</code> will create
* @param factory the new factory
*/
public void add( String id, SingleCDockableFactory factory ){
Entry old = singleIdFactories.remove( id );
if( old != null ){
factories.remove( old );
}
Entry entry = new Entry( new PresetFilter<String>( id ), factory );
factories.add( entry );
singleIdFactories.put( id, entry );
}
/**
* Registers a new factory that will load {@link SingleCDockable} with
* unique identifiers that are included by <code>ids</code>.
* @param ids the identifiers that are included
* @param factory the new factory
*/
public void add( Filter<String> ids, SingleCDockableFactory factory ){
factories.add( new Entry( ids, factory ));
}
/**
* Removes a factory from this.
* @param id the name of the factory to remove
*/
public void remove( String id ){
Entry entry = singleIdFactories.remove( id );
if( entry != null ){
factories.remove( entry );
}
}
/**
* Removes any occurrence of <code>factory</code>.
* @param factory the factory to remove
*/
public void remove( SingleCDockableFactory factory ){
Iterator<Entry> entries = factories.iterator();
while( entries.hasNext() ){
Entry next = entries.next();
if( next.factory == factory ){
entries.remove();
}
}
entries = singleIdFactories.values().iterator();
while( entries.hasNext() ){
Entry next = entries.next();
if( next.factory == factory ){
entries.remove();
}
}
}
/**
* Searches the factory which handles <code>id</code>.
* @param id the name of a factory
* @return the factory or <code>null</code>
*/
public SingleCDockableFactory getFactory( String id ){
Entry entry = singleIdFactories.get( id );
if( entry != null ){
return entry.factory;
}
for( Entry factory : factories ){
if( factory.filter.includes( id )){
return factory.factory;
}
}
return null;
}
public String getID() {
return BACKUP_FACTORY_ID;
}
/**
* Gets a list of keys for all factories known to this.
* @return the list of keys
*/
public String[] listFactories(){
Set<String> keys = singleIdFactories.keySet();
return keys.toArray( new String[ keys.size() ] );
}
/**
* Gets the set of keys for all factories known to this.
* @return the set of keys
*/
public Set<String> getFactoryIds(){
return Collections.unmodifiableSet( singleIdFactories.keySet() );
}
public void estimateLocations( CommonSingleDockableLayout layout, LocationEstimationMap children ){
// currently not supported
}
public CommonSingleDockableLayout getLayout( CommonDockable element, Map<Dockable, Integer> children ) {
CDockable dockable = element.getDockable();
if( dockable instanceof SingleCDockable ){
SingleCDockable single = (SingleCDockable)dockable;
CommonSingleDockableLayout layout = new CommonSingleDockableLayout();
layout.setId( single.getUniqueId() );
layout.setArea( single.getWorkingArea() == null ? null : single.getWorkingArea().getUniqueId() );
return layout;
}
else
throw new IllegalArgumentException( "A CommonSingleDockableFactory works only with Dockables of type SingleCDockable, but this is not a single dockable: " + element );
}
public CommonDockable layout( CommonSingleDockableLayout layout, Map<Integer, Dockable> children, PlaceholderStrategy placeholders ) {
return layout( layout, placeholders );
}
public CommonDockable layout( CommonSingleDockableLayout layout, PlaceholderStrategy placeholders ) {
SingleCDockableFactory backup = getFactory( layout.getId() );
if( backup == null )
return null;
SingleCDockable dockable = backup.createBackup( layout.getId() );
if( dockable == null )
return null;
String factoryId = dockable.intern().getFactoryID();
if( !factoryId.equals( getID() )){
throw new IllegalArgumentException( "Wrong type of dockable for unique id '" + layout.getId() + "': The backup factory created a dockable which expects a factory with type-id '" + factoryId +
"', but the call was done from a factory with type-id '" + getID() + "'" );
}
control.addDockable( dockable );
if( layout.isAreaSet()){
if( layout.getArea() != null ){
CStation<?> station = control.getStation( layout.getArea() );
if( station == null ){
DelayedWorkingAreaSetter setter = new DelayedWorkingAreaSetter( layout.getArea(), dockable, control );
setter.install();
}
else {
dockable.setWorkingArea( station );
}
}
else{
dockable.setWorkingArea( null );
}
}
return dockable.intern();
}
public CommonElementPerspective layoutPerspective( CommonSingleDockableLayout layout, Map<Integer, PerspectiveDockable> children ){
SingleCDockablePerspective dockable = new SingleCDockablePerspective( layout.getId() );
if( layout.isAreaSet() && layout.getArea() != null ){
dockable.setWorkingArea( perspective.getStation( layout.getArea() ));
}
return dockable.intern();
}
public void layoutPerspective( CommonElementPerspective perspective, CommonSingleDockableLayout layout, Map<Integer, PerspectiveDockable> children ){
// can't do anything
}
public CommonSingleDockableLayout getPerspectiveLayout( CommonElementPerspective element, Map<PerspectiveDockable, Integer> children ){
SingleCDockablePerspective dockable = (SingleCDockablePerspective)element.getElement();
CommonSingleDockableLayout layout = new CommonSingleDockableLayout();
layout.setId( dockable.getUniqueId() );
layout.setArea( dockable.getWorkingArea() == null ? null : dockable.getWorkingArea().getUniqueId() );
return layout;
}
public CommonSingleDockableLayout read( DataInputStream in, PlaceholderStrategy placeholders ) throws IOException {
Version version = Version.read( in );
CommonSingleDockableLayout layout = new CommonSingleDockableLayout();
if( version.equals( Version.VERSION_1_0_4 )){
layout.setId( in.readUTF() );
}
else if( version.equals( Version.VERSION_1_1_0 )){
layout.setId( in.readUTF() );
layout.setArea( in.readUTF() );
}
else if( version.equals( Version.VERSION_1_1_0a )){
layout.setId( in.readUTF() );
if( in.readBoolean() ){
layout.setArea( in.readUTF() );
}
else{
layout.setArea( null );
}
}
else{
throw new IOException( "Data from the future - unknown version: " + version );
}
return layout;
}
public CommonSingleDockableLayout read( XElement element, PlaceholderStrategy placeholders ) {
CommonSingleDockableLayout layout = new CommonSingleDockableLayout();
layout.setId( element.getElement( "id" ).getString() );
XElement xarea = element.getElement( "area" );
if( xarea != null ){
String area = xarea.getString();
if( "".equals( area )){
layout.setArea( null );
}
else{
layout.setArea( area );
}
}
return layout;
}
public void setLayout( CommonDockable element, CommonSingleDockableLayout layout, Map<Integer, Dockable> children, PlaceholderStrategy placeholders ) {
// can't do anything
}
public void setLayout( CommonDockable element, CommonSingleDockableLayout layout, PlaceholderStrategy placeholders ) {
// can't do anything
}
public void write( CommonSingleDockableLayout layout, DataOutputStream out ) throws IOException {
Version.write( out, Version.VERSION_1_1_0a );
out.writeUTF( layout.getId() );
String area = layout.getArea();
if( area == null ){
out.writeBoolean( false );
}
else{
out.writeBoolean( true );
out.writeUTF( area );
}
}
public void write( CommonSingleDockableLayout layout, XElement element ) {
element.addElement( "id" ).setString( layout.getId() );
XElement xarea = element.addElement( "area" );
if( layout.getArea() != null ){
xarea.setString( layout.getArea() );
}
}
/**
* One backup factory of a {@link CommonSingleDockableFactory}
*/
private static class Entry{
/** the filter to apply before {@link #factory} can be used */
public final Filter<String> filter;
/** the factory represented by this entry */
public final SingleCDockableFactory factory;
/**
* Creates a new entry.
* @param filter the filter to use
* @param factory the new factory
*/
public Entry( Filter<String> filter, SingleCDockableFactory factory ){
if( filter == null ){
throw new IllegalArgumentException( "filter must not be null" );
}
if( factory == null ){
throw new IllegalArgumentException( "factory must not be null" );
}
this.filter = filter;
this.factory = factory;
}
}
}