bibliothek.gui.dock.layout.PredefinedDockSituation Maven / Gradle / Ivy
/*
* 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) 2008 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.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import bibliothek.gui.DockController;
import bibliothek.gui.DockStation;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.DockElement;
import bibliothek.gui.dock.DockFactory;
import bibliothek.gui.dock.perspective.PerspectiveDockable;
import bibliothek.gui.dock.perspective.PerspectiveElement;
import bibliothek.gui.dock.perspective.PredefinedPerspective;
import bibliothek.gui.dock.station.support.PlaceholderStrategy;
import bibliothek.gui.dock.util.DockUtilities;
import bibliothek.gui.dock.util.extension.ExtensionManager;
import bibliothek.util.Version;
import bibliothek.util.xml.XElement;
import bibliothek.util.xml.XException;
/**
* A {@link DockSituation} that does not load or store all {@link DockElement DockElements}.
* All elements which are registered by {@link #put(DockElement)} are stored in an
* internal list. On writing, just a unique id is written to the stream.
* A {@link DockFactory} is still necessary for these elements, but the factory may
* just do nothing.
* @author Benjamin Sigg
*/
public class PredefinedDockSituation extends DockSituation {
/** A mapping from ids to a list of elements which must not be created by a factory */
private Map stringToElement = new HashMap();
/** A mapping from a list of elements to their ids */
private Map elementToString = new HashMap();
private static final String KNOWN = "predefined";
private static final String UNKNOWN = "delegate_";
/** backup factories for elements that should be in the cache, but are missing */
private Map>> backups =
new HashMap>>();
private final PreloadFactory factory = new PreloadFactory();
/**
* Creates a new {@link DockSituation}, uses controller
to access the
* {@link ExtensionManager} and install additional {@link DockFactory}s.
* @param controller the controller in whose realm this situation will be used
*/
public PredefinedDockSituation( DockController controller ){
super( controller );
}
/**
* Creates a new {@link PredefinedPerspective}. This perspective will write the layout in
* such a way that a {@link PredefinedDockSituation} can read it again.
* Please note that clients need to register {@link DockElement}s using {@link PredefinedPerspective#put(String, PerspectiveElement)}
* before the perspective can be used. The {@link DockElement}s registered to this
by using {@link #put(String, DockElement)}
* are ignored by the perspective.
*/
@Override
public PredefinedPerspective createPerspective(){
return new PredefinedPerspective( this ){
private PreloadFactory preload;
{
preload = new PreloadFactory(this);
}
protected String getID( PerspectiveElement element ){
return PredefinedDockSituation.this.getID( element, this );
}
protected DockFactory, ?, ?> getFactory( String id ){
DockFactory, ?, ?> factory = PredefinedDockSituation.this.getFactory( id );
if( factory == PredefinedDockSituation.this.factory ){
return preload;
}
return factory;
}
};
}
/**
* Adds a backup factory to this situation. A backup factory is used when
* an element should be in the cache, but is missing. The backup factory
* receives a {@link BackupFactoryData} object, the identifier of that
* object does not have to be stored by factory
. The
* factory has only to look at the {@link BackupFactoryData#getData() data}-property.
* This {@link PredefinedDockSituation} will set the identifier whenever
* a method of factory
is called, that has a {@link BackupFactoryData}
* as parameter.
* @param factory a backup factory
*/
public void addBackup( DockFactory extends DockElement, ?, ? extends BackupFactoryData>> factory ){
backups.put( UNKNOWN + factory.getID(), factory );
}
/**
* Removes the backup factory with the name id
.
* @param id the id of the factory which should be removed
*/
public void removeBackup( String id ){
backups.remove( UNKNOWN + id );
}
/**
* Registers an element at this situation. When a stream is read, this
* element will be returned instead of a newly created element (assuming
* that the element was written into the stream). The key for
* the element is generated automatically.
* Note: the order in which elements are added is important as the
* unique key depends on the order.
* @param element the element
* @deprecated use {@link #put(String, DockElement)} instead
*/
@Deprecated
public void put( DockElement element ){
put( String.valueOf( stringToElement.size() ), element );
}
/**
* Registers an element at this situation. When a stream is read, this
* element will be returned instead a newly created element (assuming
* that the element was written into the stream).
* @param key the key of the element
* @param element the element
* @throws IllegalArgumentException if the key is already used
*/
public void put( String key, DockElement element ){
if( stringToElement.containsKey( key ))
throw new IllegalArgumentException( "Key does already exist: " + key );
stringToElement.put( key, element );
elementToString.put( element, key );
}
/**
* Tells whether the layout of element
itself should be stored
* or loaded, that will not prevent the element
from showing
* up but from changing its content. The default implementation returns
* always true
. This method is intended to be overridden by
* subclasses.
* @param element the element whose contents might or might not be stored
* or loaded
* @return true
if the contents should be handled, false
* if they should be discarded
*/
protected boolean shouldLayout( DockElement element ){
return true;
}
/**
* Tells whether the layout of element
itself should be stored
* or loaded, that will not prevent the element
from showing
* up but from changing its content. The default implementation uses the maps
* of perspective
and this {@link DockSituation} to convert element
* into a preset {@link DockElement} and calls {@link #shouldLayout(DockElement)}. If the
* element is not found, true
is returned.
* @param element the element whose contents might or might not be stored
* or loaded
* @param perspective the perspective for which the question has to be answered
* @return true
if the contents should be handled, false
* if they should be discarded
*/
protected boolean shouldLayout( PerspectiveElement element, PredefinedPerspective perspective ){
String key = perspective.get( element );
if( key != null ){
DockElement dock = stringToElement.get( key );
if( dock != null ){
return shouldLayout( dock );
}
}
return true;
}
@Override
protected DockLayoutInfo fillMissing( DockLayoutInfo info ) {
DockLayout> layout = info.getDataLayout();
if( KNOWN.equals( layout.getFactoryID() )){
PredefinedLayout preloaded = (PredefinedLayout)layout.getData();
DockLayoutInfo delegate = preloaded.getDelegate();
DockLayoutInfo newDelegate = null;
if( delegate.getKind() == DockLayoutInfo.Data.BYTE ){
newDelegate = fillMissingStream( preloaded );
}
else if( delegate.getKind() == DockLayoutInfo.Data.XML ){
newDelegate = fillMissingXML( preloaded );
}
if( newDelegate != null ){
info = new DockLayoutInfo( new DockLayout(
KNOWN, new PredefinedLayout( preloaded.getPredefined(), newDelegate )));
}
}
return info;
}
/**
* Given a set of Dockable
s this method
* estimates which of them will be visible once composition
* is applied.
* @param the kind of elements to check
* @param base a collection of Dockable
s in no specific
* order and with no restrictions
* @param composition location information for various elements
* @return A subset of base
with those elements which will
* be visible once this situation converts composition
*/
public Set listVisible( Collection base, DockLayoutComposition composition ){
Set result = new HashSet();
listVisible( base, composition, result );
return result;
}
@SuppressWarnings("unchecked")
private void listVisible( final Collection base, DockLayoutComposition composition, final Set result ){
DockLayoutInfo info = composition.getLayout();
if( info.getKind() == DockLayoutInfo.Data.DOCK_LAYOUT ){
DockLayout> layout = info.getDataLayout();
// preloaded element with key
if( KNOWN.equals( layout.getFactoryID() )){
PredefinedLayout preload = (PredefinedLayout)layout.getData();
String key = preload.getPredefined();
DockElement element = stringToElement.get( key );
if( element != null ){
if( base.contains( element )){
result.add( (D)element );
}
if( composition.isIgnoreChildren() ){
DockUtilities.visit( element, new DockUtilities.DockVisitor(){
@Override
public void handleDockable( Dockable dockable ) {
if( base.contains( dockable )){
result.add( (D)dockable );
}
}
@Override
public void handleDockStation( DockStation station ) {
if( base.contains( station )){
result.add( (D)station );
}
}
});
}
}
}
}
// check all children
for( DockLayoutComposition child : composition.getChildren() ){
listVisible( base, child, result );
}
}
/**
* Lists for all keys that can be found in composition
its
* estimated location.
* Note: This method will call {@link #estimateLocations(DockLayoutComposition)}
* to get the most recent locations
* @param composition some composition to search for keys and locations
* @param missingOnly if set, then only locations of keys for which
* no {@link DockLayout} is set are reported. This are the keys which most
* likely will be ignored when calling {@link #convert(DockLayoutComposition)}
* @return the map of keys and positions, might be empty
*/
public Map listEstimatedLocations( DockLayoutComposition composition, boolean missingOnly ){
return listEstimatedLocations( composition, composition.getLayout().getLocation(), missingOnly );
}
/**
* Lists for all keys that can be found in composition
its
* estimated location.
* Note: This method will call {@link #estimateLocations(DockLayoutComposition)}
* to get the most recent locations
* @param composition some composition to search for keys and locations
* @param location the location of composition
itself
* @param missingOnly if set, then only locations of keys for which
* no {@link DockLayout} is set are reported. This are the keys which most
* likely will be ignored when calling {@link #convert(DockLayoutComposition)}
* @return the map of keys and positions, might be empty
*/
public Map listEstimatedLocations( DockLayoutComposition composition, DockableProperty location, boolean missingOnly ){
estimateLocations( composition, location );
Map map = new HashMap();
listEstimatedLocations( composition, missingOnly, map );
if( location != null ){
String key = getKey( composition, missingOnly );
if( key != null ){
map.put( key, location );
}
}
return map;
}
private void listEstimatedLocations( DockLayoutComposition composition, boolean missingOnly, Map map ){
DockableProperty location = composition.getLayout().getLocation();
if( location != null ){
String key = getKey( composition, missingOnly );
if( key != null){
map.put( key, location );
}
}
List children = composition.getChildren();
if( children != null ){
for( DockLayoutComposition child : children ){
listEstimatedLocations( child, missingOnly, map );
}
}
}
/**
* Gets a map containing some or all of the named layouts.
* @param composition some composition to analyze
* @param missingOnly if set, then only locations of keys for which
* no {@link DockLayout} is set are reported. This are the keys which most
* likely will be ignored when calling {@link #convert(DockLayoutComposition)}
* @return the map of keys and layouts, might be empty
*/
public Map listLayouts( DockLayoutComposition composition, boolean missingOnly ){
Map map = new HashMap();
listLayouts( composition, missingOnly, map );
return map;
}
private void listLayouts( DockLayoutComposition composition, boolean missingOnly, Map map ){
String key = getKey( composition, missingOnly );
if( key != null){
map.put( key, composition );
}
List children = composition.getChildren();
if( children != null ){
for( DockLayoutComposition child : children ){
listLayouts( child, missingOnly, map );
}
}
}
/**
* Gets the name of element which is represented by composition
.
* @param composition the composition whose element key is searched
* @param missingOnly if set, then the key will only be returned if composition
* is not fully loaded
* @return the key or null
*/
private String getKey( DockLayoutComposition composition, boolean missingOnly ){
DockLayoutInfo layout = composition.getLayout();
if( layout.getKind() != DockLayoutInfo.Data.DOCK_LAYOUT )
return null;
if( !KNOWN.equals( layout.getDataLayout().getFactoryID() ))
return null;
PredefinedLayout preloaded = (PredefinedLayout)layout.getDataLayout().getData();
if( missingOnly && preloaded.getDelegate().getKind() == DockLayoutInfo.Data.DOCK_LAYOUT ){
// if there is such a Dockable registered then it is not missing...
if( stringToElement.containsKey( preloaded.getPredefined() )){
return null;
}
}
String key = preloaded.getPredefined();
return key;
}
/**
* Tries to read the byte data in layout
.
* @param layout the layout to read
* @return either a new info or null
if the data could
* not be read
*/
@SuppressWarnings("unchecked")
private DockLayoutInfo fillMissingStream( PredefinedLayout layout ){
byte[] bytes = layout.getDelegate().getDataByte();
try{
DataInputStream in = new DataInputStream( new ByteArrayInputStream( bytes ));
String factoryId = in.readUTF();
DockFactory factory = (DockFactory)getFactory( factoryId );
DockLayoutInfo info = null;
if( factory == null ){
DockFactory,?,BackupFactoryData>> backup = getBackup( factoryId );
if( backup != null ){
BackupFactoryData
© 2015 - 2025 Weber Informatics LLC | Privacy Policy