Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* 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
* [email protected]
* CH - Switzerland
*/
package bibliothek.gui.dock.layout;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
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 bibliothek.gui.DockController;
import bibliothek.gui.DockStation;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.DefaultDockable;
import bibliothek.gui.dock.DockElement;
import bibliothek.gui.dock.DockFactory;
import bibliothek.gui.dock.FlapDockStation;
import bibliothek.gui.dock.SplitDockStation;
import bibliothek.gui.dock.StackDockStation;
import bibliothek.gui.dock.dockable.DefaultDockableFactory;
import bibliothek.gui.dock.perspective.Perspective;
import bibliothek.gui.dock.perspective.PerspectiveElement;
import bibliothek.gui.dock.station.flap.FlapDockStationFactory;
import bibliothek.gui.dock.station.screen.ScreenDockStationFactory;
import bibliothek.gui.dock.station.split.SplitDockStationFactory;
import bibliothek.gui.dock.station.stack.StackDockStationFactory;
import bibliothek.gui.dock.station.support.PlaceholderStrategy;
import bibliothek.gui.dock.util.DockUtilities;
import bibliothek.gui.dock.util.extension.ExtensionManager;
import bibliothek.gui.dock.util.extension.ExtensionName;
import bibliothek.util.Path;
import bibliothek.util.Todo;
import bibliothek.util.Version;
import bibliothek.util.Todo.Compatibility;
import bibliothek.util.Todo.Priority;
import bibliothek.util.xml.XAttribute;
import bibliothek.util.xml.XElement;
import bibliothek.util.xml.XException;
/**
* A DockSituation is a converter: the relationship of {@link DockStation}s and {@link Dockable}s,
* the position of Dockables and other information are converted into a
* stream of bytes. The other direction, read a stream and create Dockables and DockStations, is also possible.
* @author Benjamin Sigg
*/
public class DockSituation {
/** Name for an {@link ExtensionName} to load additional {@link DockFactory}s */
public static final Path DOCK_FACTORY_EXTENSION = new Path("dock.DockSituation.DockFactory");
/** Name for an {@link ExtensionName} to load additional {@link AdjacentDockFactory}s */
public static final Path ADJACENT_DOCK_FACTORY_EXTENSION = new Path("dock.DockSituation.AdjacentDockFactory");
/** Name of a parameter of an {@link ExtensionName} pointing to this */
public static final String EXTENSION_PARAM = "situation";
/** the factories used to create new {@link DockElement elements}*/
private Map> factories = new HashMap>();
/** the factory used when no {@link DockFactory} is available */
private MissingDockFactory missingFactory;
/** a set of additional factories for the {@link DockElement}s */
private Map> adjacent = new HashMap>();
/** the factory used when no {@link AdjacentDockFactory} is available */
private MissingDockFactory missingAdjacent;
/** a filter for elements which should be ignored */
private DockSituationIgnore ignore;
/** strategy used to filter placeholders in the intermediate format */
private PlaceholderStrategy intermediatePlaceholders;
/** strategy used to filter placeholders when converting the intermediate format to real {@link DockElement}s */
private PlaceholderStrategy placeholders;
/**
* Constructs a new DockSituation and sets some factories which are
* used to create new {@link DockElement DockElements}. Please note that this
* constructor does not add the default factories, hence it should be used with care.
* @param factories the factories
*/
public DockSituation( DockFactory,?,?>...factories ){
for( DockFactory,?,?> factory : factories )
this.factories.put( getID( factory ), factory );
}
/**
* Constructs a new DockSituation. Factories for {@link DefaultDockable},
* {@link SplitDockStation}, {@link StackDockStation} and
* {@link FlapDockStation} will be preinstalled.
* @param controller {@link DockController} in whose realm this {@link DockSituation} will be used, the
* controller is used to access the {@link ExtensionManager} and load additional factories
*/
public DockSituation( DockController controller ){
this(
new DefaultDockableFactory(),
new SplitDockStationFactory(),
new StackDockStationFactory(),
new FlapDockStationFactory());
@SuppressWarnings("rawtypes")
List factories = controller.getExtensions().load( new ExtensionName( DOCK_FACTORY_EXTENSION, DockFactory.class, EXTENSION_PARAM, this ) );
for( DockFactory,?,?> factory : factories ){
add( factory );
}
@SuppressWarnings("rawtypes")
List adjacent = controller.getExtensions().load( new ExtensionName( ADJACENT_DOCK_FACTORY_EXTENSION, AdjacentDockFactory.class, EXTENSION_PARAM, this ) );
for( AdjacentDockFactory> factory : adjacent ){
addAdjacent( factory );
}
}
/**
* Creates a new {@link Perspective} that uses the settings made on this {@link DockSituation}. Changes on the
* properties of this {@link DockSituation} will be noticed and used by the created {@link Perspective}. However
* changes on the properties of the {@link Perspective} will not influence this {@link DockSituation}.
* Note that subclasses may create {@link Perspective}s that need the client to make additional settings before
* it can be used.
* @return the new perspective
*/
public Perspective createPerspective(){
return new Perspective( this ){
@Override
protected String getID( PerspectiveElement element ){
return DockSituation.this.getID( element );
}
@Override
protected DockFactory,?,?> getFactory( String id ){
return DockSituation.this.getFactory( id );
}
};
}
/**
* Sets a filter which decides, which elements (stations and dockables)
* are stored.
* @param ignore the filter or null
*/
public void setIgnore( DockSituationIgnore ignore ) {
this.ignore = ignore;
}
/**
* Gets the filter which decides, which elements are stored.
* @return the filter or null
*/
public DockSituationIgnore getIgnore() {
return ignore;
}
/**
* Sets a strategy for deleting invalid placeholders.
* @param placeholders the strategy, null for keeping all placeholders
*/
public void setPlaceholderStrategy( PlaceholderStrategy placeholders ){
this.placeholders = placeholders;
}
/**
* Gets the current strategy for removing invalid placeholders.
* @return the strategy, may be null
*/
public PlaceholderStrategy getPlaceholderStrategy(){
return placeholders;
}
/**
* Sets the strategy for deleting invalid placeholders in the intermediate format
* @param intermediatePlaceholders the strategy, can be null
*/
public void setIntermediatePlaceholders( PlaceholderStrategy intermediatePlaceholders ){
this.intermediatePlaceholders = intermediatePlaceholders;
}
/**
* Gets the strategy for deleting invalid placeholders in the intermediate format.
* @return the intermediate strategy, can be null
*/
public PlaceholderStrategy getIntermediatePlaceholders(){
return intermediatePlaceholders;
}
/**
* Gets a placeholder for element using the current {@link PlaceholderStrategy}.
* @param element some element, not null
* @return the placeholder, can be null
*/
protected Path getPlaceholder( DockElement element ){
if( placeholders == null ){
return null;
}
Dockable dockable = element.asDockable();
if( dockable == null ){
return null;
}
return placeholders.getPlaceholderFor( dockable );
}
/**
* Adds a factory
* @param factory the additional factory
*/
public void add( DockFactory,?,?> factory ){
factories.put( getID( factory ), factory );
}
/**
* Adds an adjacent factory
* @param factory the new factory
*/
public void addAdjacent( AdjacentDockFactory> factory ){
adjacent.put( getAdjacentID( factory ), factory );
}
/**
* Sets a factory which is used whenever no ordinary {@link DockFactory}
* can be found to read something. The missingFactory can convert
* the input to any {@link Object} it likes, but if a missing factory
* is later added to this situation, then that object needs to be casted
* into the object used by the original factory. So when working with
* a {@link MissingDockFactory} handling different types of layout-data
* needs to be done very carefully. Note that this factory is only used to convert
* byte-stream or xml data to the intermediate format. It cannot create any {@link Dockable} or
* {@link DockElement} nor store any data.
* @param missingFactory the factory, can be null
*/
public void setMissingFactory( MissingDockFactory missingFactory ) {
this.missingFactory = missingFactory;
}
/**
* Gets the factory which is used when another factory is missing.
* @return the factory replacing missing factories, can be null
* @see #setMissingFactory(MissingDockFactory)
*/
public MissingDockFactory getMissingFactory() {
return missingFactory;
}
/**
* Sets a factory which is used when a {@link AdjacentDockFactory} is missing.
* There are the same issues with this factory than with the one used
* by {@link #setMissingFactory(MissingDockFactory)}.
* @param missingAdjacent the new factory, can be null
* @see #setMissingFactory(MissingDockFactory)
*/
public void setMissingAdjacentFactory( MissingDockFactory missingAdjacent ) {
this.missingAdjacent = missingAdjacent;
}
/**
* Gets the factory which is used when another {@link AdjacentDockFactory}
* is missing.
* @return the factory, can be null
* @see #setMissingAdjacentFactory(MissingDockFactory)
*/
public MissingDockFactory getMissingAdjacentFactory() {
return missingAdjacent;
}
/**
* Converts the layout of element and all its children into a
* {@link DockLayoutComposition}.
* @param element the element to convert
* @return the composition or null if the element is ignored
* @throws IllegalArgumentException if one element has an unknown id of
* a {@link DockFactory}.
* @throws ClassCastException if an element does not specify the correct
* {@link DockFactory}.
*/
@SuppressWarnings("unchecked")
public DockLayoutComposition convert( DockElement element ){
if( ignoreElement( element ))
return null;
String id = getID( element );
DockFactory factory = (DockFactory)getFactory( id );
if( factory == null )
throw new IllegalArgumentException( "Unknown factory-id: " + element.getFactoryID() );
DockStation station = element.asDockStation();
Map ids = new HashMap();
List children = new ArrayList();
boolean ignore = false;
if( station != null ){
ignore = ignoreChildren( station );
if( !ignore ){
int index = 0;
for( int i = 0, n = station.getDockableCount(); i layout = new DockLayout if the element should not be saved
*/
protected boolean ignoreElement( DockElement element ){
if( ignore == null )
return false;
return ignore.ignoreElement( element );
}
/**
* Tells whether to ignore the children of the station when saving or not. If the children
* are ignored, no factories are needed for them. This implementation forwards
* the call to the {@link DockSituationIgnore} of this situation.
* @param station the station whose children might be ignored
* @return true if the station is saved as having no children
*/
protected boolean ignoreChildren( DockStation station ){
if( ignore == null )
return false;
return ignore.ignoreChildren( station );
}
/**
* Gets the id of the factory which is needed to write (and later
* read) element
* @param element the element to read
* @return the id of the factory
* @see #getID(DockFactory)
* @see #getFactory(String)
*/
protected String getID( PerspectiveElement element ){
return element.getFactoryID();
}
/**
* Gets the id of the factory which is needed to write (and later
* read) element
* @param element the element to read
* @return the id of the factory
* @see #getID(DockFactory)
* @see #getFactory(String)
*/
protected String getID( DockElement element ){
return element.getFactoryID();
}
/**
* Gets the id of factory. The default behavior is just to
* return {@link DockFactory#getID()}. Note that this method should be
* a bijection to {@link #getFactory(String)}.
* @param factory the factory whose id is needed
* @return the id of the factory
*/
protected String getID( DockFactory,?,?> factory ){
return factory.getID();
}
/**
* Reads the id of the factory which was used to create info.
* This returns the id of the factory as it is used internally by this
* {@link DockSituation}.
* @param info the info to check
* @return the id of a factory or null if info does not contain
* data
* @throws IllegalArgumentException if the data of info is in the wrong format
* @throws XException if the data of info is in the wrong format
*/
protected String getFactoryID( DockLayoutInfo info ){
if( info.getKind() == DockLayoutInfo.Data.BYTE ){
try{
DataInputStream in = new DataInputStream( new ByteArrayInputStream( info.getDataByte() ));
String result = in.readUTF();
in.close();
return result;
}
catch( IOException ex ){
throw new IllegalArgumentException( "byte entry not in the correct format", ex );
}
}
if( info.getKind() == DockLayoutInfo.Data.XML ){
return info.getDataXML().getString( "factory" );
}
if( info.getKind() == DockLayoutInfo.Data.DOCK_LAYOUT ){
return info.getDataLayout().getFactoryID();
}
return null;
}
/**
* Tells what identifier is used for factory in the
* {@link DockLayoutComposition}.
* This method just calls {@link #getID(DockFactory)}, but
* {@link #getID(DockFactory)} is intended for internal use while this
* method is intended to be used by clients which read out a {@link DockLayoutComposition}.
* @param factory the factory which might be used
* @return the identifier
* @see #getID(DockFactory)
*/
public String convertFactoryId( DockFactory,?,?> factory ){
return getID( factory );
}
/**
* Tells what identifier the {@link DockFactory} has, for which the
* identifier id is used within a {@link DockLayoutComposition}.
* This method just calls {@link #getFactoryID(String)}, but while
* {@link #getFactoryID(String)} is intended for internal use, this method
* is intended for clients.
* @param id an identifier found in a {@link DockLayoutComposition}
* @return the identifer of a {@link DockFactory}
*/
public String convertFactoryId( String id ){
return getFactoryID( id );
}
/**
* Transforms an id read from a stream to the id of the factory which
* would be used. This method must fulfill one contract:
* DockFactory factory = ...
* factory.getID().equals( getFactoryID( getID( factory )));
* @param id the id read from a stream
* @return the id of the original factory
*/
protected String getFactoryID( String id ){
return id;
}
/**
* Gets the id of factory. The default behavior is just to
* return {@link DockFactory#getID()}. Note that this method should be
* a bijection to {@link #getAdjacentFactory(String)}.
* @param factory the factory whose id is needed
* @return the id of the factory
*/
protected String getAdjacentID( AdjacentDockFactory> factory ){
return factory.getID();
}
/**
* Transforms an id read from a stream to the id of the adjacent factory which
* would be used. This method must fulfill one contract:
* AdjacentDockFactory factory = ...
* factory.getID().equals( getFactoryID( getAdjacentID( factory )));
* @param id the id read from a stream
* @return the id of the original factory
*/
protected String getAdjacentFactoryID( String id ){
return id;
}
/**
* Gets the factory which has the given id. Note that this
* method should be a bijection to {@link #getID(DockFactory)}. The
* default behavior compares id with the
* {@link #getID(DockFactory)}.
* @param id the name of the factory
* @return the factory or null if no factory has this id
*/
@Todo( compatibility=Compatibility.BREAK_MINOR, priority=Priority.MAJOR, target=Todo.Version.VERSION_1_1_3,
description="remove the legacy code that filters out identifiers that look like 'secure ...'. Instead 'result' can be returned directly." )
public DockFactory extends DockElement,?,?> getFactory( String id ){
DockFactory, ?, ?> result = factories.get( id );
if( result == null ){
String base = null;
if( id.startsWith( "delegate_secure " )){
base = id.substring( "delegate_secure ".length() );
id = "delegate_" + base;
}
else if( id.startsWith( "secure " )){
base = id.substring( "secure ".length() );
id = base;
}
if( FlapDockStationFactory.ID.equals( base) ||
ScreenDockStationFactory.ID.equals( base ) ||
StackDockStationFactory.ID.equals( base ) ||
SplitDockStationFactory.ID.equals( base )){
result = factories.get( id );
}
}
return result;
}
/**
* Gets the adjacent factory which has the given id. Note that this
* method should be a bijection to {@link #getID(DockFactory)}. The
* default behavior compares id with the
* {@link #getID(DockFactory)}.
* @param id the name of the factory
* @return the factory or null if no factory has this id
*/
public AdjacentDockFactory> getAdjacentFactory( String id ){
return adjacent.get( id );
}
/**
* Gets all the adjacent factories that are currently registered at this {@link DockSituation},
* the returned {@link Map} is unmodifiable.
* @return an unmodifiable map containing all {@link AdjacentDockFactory}s.
*/
public Map> getAdjacentFactorys(){
return Collections.unmodifiableMap( adjacent );
}
/**
* Tells what identifier was associated with composition when it was
* stored by a {@link DockSituation} of this type. The default implementation always
* returns null.
* @param composition some element that was created by this or a similar {@link DockSituation}
* @return the identifier that was associated with composition
*/
public String getIdentifier( DockLayoutComposition composition ){
return null;
}
}