/* * 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) 2011 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.station.split; import java.util.HashSet; import java.util.Set; import bibliothek.gui.Dockable; import bibliothek.gui.dock.SplitDockStation; import bibliothek.gui.dock.SplitDockStation.Orientation; import bibliothek.gui.dock.station.support.PlaceholderMap; import bibliothek.gui.dock.station.support.PlaceholderStrategy; import bibliothek.gui.dock.station.support.PlaceholderMap.Key; import bibliothek.util.Path; /** * Supporting class for {@link SplitDockStation} and {@link SplitDockPerspective}, allows to create * and read {@link PlaceholderMap}s. * @author Benjamin Sigg * @param <P> the kind of station or perspective using this converter * @param <N> the type of a single node * @param <D> the type representing a {@link Dockable} */ public abstract class AbstractSplitPlaceholderConverter<P, N, D> { /** the station for which this converter is used */ private P station; /** * Creates a new converter * @param station the station for which the converter will be used */ public AbstractSplitPlaceholderConverter( P station ){ this.station = station; } /** * Gets the station for which this converter is used. * @return the station */ public P getStation(){ return station; } /** * Converts the station of this converter into a {@link PlaceholderMap}. * @return the map * @see SplitDockStation#getPlaceholders() */ public PlaceholderMap getPlaceholders(){ final PlaceholderMap result = new PlaceholderMap( new Path( "dock.SplitDockStation" ), 0 ); handleChild( result, getRoot( station ), "" ); return result; } /** * Reads <code>map</code> and updates the contents of the {@link SplitDockStation} that is * related to this converter. * @param map the map to read * @see SplitDockStation#setPlaceholders(PlaceholderMap) */ public void setPlaceholders( PlaceholderMap map ){ map = map.filter( getPlaceholderStrategy( station ) ); Key[] keys = map.getPlaceholders(); BuildNode root = new BuildNode(); for( Key key : keys ){ handleEntry( key, map, root ); } SplitDockTree<D> tree = createTree(); root.collapse( tree ); dropTree( station, tree ); } /** * Gets the root node of the tree that describes the layout of <code>station</code>. * @param station the station whose root node is searched * @return the root node */ protected abstract N getRoot( P station ); /** * Gets the {@link PlaceholderStrategy} that is used by <code>station</code> to filter * its children. * @param station the station whose {@link PlaceholderStrategy} is searched * @return the strategy */ protected abstract PlaceholderStrategy getPlaceholderStrategy( P station ); /** * Tells whether <code>node</code> is a root node. * @param node the node to check * @return <code>true</code> if <code>node</code> is a root node */ protected abstract boolean isRoot( N node ); /** * Tells whether <code>node</code> is an intermediate node. * @param node the node to check * @return <code>true</code> if <code>node</code> is an intermediate node */ protected abstract boolean isNode( N node ); /** * Tells whether <code>node</code> is a leaf node. * @param node the node to check * @return <code>true</code> if <code>node</code> is a leaf node */ protected abstract boolean isLeaf( N node ); /** * Tells whether <code>node</code> is a placeholder node. * @param node the node to check * @return <code>true</code> if <code>node</code> is a placeholder node */ protected abstract boolean isPlaceholder( N node ); /** * Gets all placeholders that are associated with <code>node</code>. * @param node the node whose placeholders are searched * @return the placeholders */ protected abstract Path[] getPlaceholders( N node ); /** * Gets the {@link PlaceholderMap} which is associated with <code>node</code>. * @param node the node whose {@link PlaceholderMap} is searched * @return the map or <code>null</code> */ protected abstract PlaceholderMap getPlaceholderMap( N node ); /** * Gets the unique identifier that was assigned to <code>node</code>. * @param node some node whose id is searched * @return the unique identifier */ protected abstract long getId( N node ); /** * Gets the child of the {@link #isRoot(Object) root node} <code>root</code>. * @param root a root node * @return the only child of <code>root</code> */ protected abstract N getRootChild( N root ); /** * Gets the left child of the {@link #isNode(Object) intermediate node} <code>node</code>. * @param node an intermediate node * @return the left child of <code>node</code> */ protected abstract N getLeftChild( N node ); /** * Gets the right child of the {@link #isNode(Object) intermediate node} <code>node</code>. * @param node an intermediate node * @return the right child of <code>node</code> */ protected abstract N getRightChild( N node ); /** * Gets the divider location of the {@link #isNode(Object) intermediate node} <code>node</code>. * @param node an intermediate node * @return the divider location of <code>node</code> */ protected abstract double getDivider( N node ); /** * Gets the orientation of the {@link #isNode(Object) intermediate node} <code>node</code>. * @param node an intermediate node * @return the orientation of <code>node</code> */ protected abstract Orientation getOrientation( N node ); /** * Gets the dockable of the {@link #isLeaf(Object) leaf node} <code>leaf</code>. * @param leaf a leaf node * @return the dockable of <code>leaf</code> */ protected abstract D getDockable( N leaf ); /** * Gets a placeholder that is to be used for <code>dockable</code>. * @param dockable some item whose placeholder is searched * @return the placeholder, can be <code>null</code> */ protected abstract Path getPlaceholderFor( D dockable ); /** * Creates a new {@link SplitDockTree} which will be used for {@link #dropTree(Object, SplitDockTree)}. * @return the new tree, not <code>null</code> */ protected abstract SplitDockTree<D> createTree(); /** * Updates <code>station</code> such that its layout looks as described by <code>tree</code>. * @param station the station whose layout gets updated * @param tree the new layout */ protected abstract void dropTree( P station, SplitDockTree<D> tree ); private Set<Path> handleChild( PlaceholderMap result, N node, String path ){ if( isRoot( node ) ){ return handleRoot( result, node, path ); } else if( isNode( node ) ){ return handleNode( result, node, path ); } else if( isLeaf( node ) ){ return handleLeaf( result, node, path ); } else if( isPlaceholder( node ) ){ return handlePlaceholder( result, node, path ); } else{ return null; } } private Key handleBase( PlaceholderMap result, N node, String path, Set<Path> allPlaceholders, Set<Path> localPlaceholders ){ Path[] array = getPlaceholders( node ); PlaceholderMap map = getPlaceholderMap( node ); if( array != null ){ for( Path key : array ){ allPlaceholders.add( key ); if( localPlaceholders == null ){ localPlaceholders = new HashSet<Path>(); } localPlaceholders.add( key ); } } if( !allPlaceholders.isEmpty() ){ Key key = result.newKey( allPlaceholders.toArray( new Path[ allPlaceholders.size() ] ) ); result.putLong( key, path + ".id", getId( node ) ); if( map != null ){ result.putMap( key, path + ".map", map.copy() ); } if( localPlaceholders != null && !localPlaceholders.isEmpty() ){ Key arrayKey = result.newKey( localPlaceholders.toArray( new Path[ localPlaceholders.size() ] ) ); result.put( arrayKey, path + ".array", true ); } return key; } return null; } private Set<Path> handleRoot( PlaceholderMap result, N root, String path ){ Set<Path> set = handleChild( result, getRootChild( root ), path + "0" ); if( set != null ){ Key key = handleBase( result, root, path, set, null ); if( key != null ){ result.putString( key, path + ".type", "r" ); return set; } } return null; } private Set<Path> handleNode( PlaceholderMap result, N node, String path ){ Set<Path> left = handleChild( result, getLeftChild( node ), path + "0" ); Set<Path> right = handleChild( result, getRightChild( node ), path + "1" ); if( left == null && right == null ){ return null; } Set<Path> all; if( left == null ){ all = right; } else if( right == null ){ all = left; } else{ all = left; all.addAll( right ); } Key key = handleBase( result, node, path, all, null ); if( key != null ){ result.putString( key, path + ".type", "n" ); result.putDouble( key, path + ".divider", getDivider( node ) ); result.putBoolean( key, path + ".orientation", getOrientation( node ) == Orientation.HORIZONTAL ); return all; } return null; } private Set<Path> handlePlaceholder( PlaceholderMap result, N placeholder, String path ){ Set<Path> set = new HashSet<Path>(); Key key = handleBase( result, placeholder, path, set, null ); if( key != null ){ result.putString( key, path + ".type", "p" ); return set; } return null; } private Set<Path> handleLeaf( PlaceholderMap result, N leaf, String path ){ Set<Path> set = new HashSet<Path>(); Set<Path> local = new HashSet<Path>(); D dockable = getDockable( leaf ); if( dockable != null ){ Path key = getPlaceholderFor( dockable ); if( key != null ){ set.add( key ); local.add( key ); } } Key key = handleBase( result, leaf, path, set, local ); if( key != null ){ result.putString( key, path + ".type", "l" ); return set; } return null; } private void handleEntry( Key key, PlaceholderMap map, BuildNode root ){ for( String item : map.getKeys( key )){ BuildNode node = root.get( path( item )); String itemKey = key( item ); if( "type".equals( itemKey )){ node.type = map.getString( key, item ); } else if( "id".equals( itemKey )){ node.id = map.getLong( key, item ); } else if( "map".equals( itemKey )){ node.map = map.getMap( key, item ); } else if( "array".equals( itemKey )){ node.placeholders = key.getPlaceholders(); } else if( "divider".equals( itemKey )){ node.divider = map.getDouble( key, item ); } else if( "orientation".equals( itemKey )){ node.orientation = map.getBoolean( key, item ); } } } private String path( String item ){ return item.substring( 0, item.indexOf( '.' ) ); } private String key( String item ){ return item.substring( item.indexOf( '.' )+1 ); } /** * A representation for a {@link SplitNode} helping to build up an initial {@link DockableSplitDockTree}. * @author Benjamin Sigg */ private class BuildNode{ private BuildNode[] children; private long id = -1; private String type; private Path[] placeholders; private PlaceholderMap map; private boolean orientation; private double divider = 0.5; public BuildNode get( String path ){ return get( path, 0 ); } private BuildNode get( String path, int index ){ if( index == path.length() ){ return this; } if( '0' == path.charAt( index )){ return get(0).get( path, index+1 ); } else{ return get(1).get( path, index+1 ); } } private BuildNode[] createArray( int length ){ return (BuildNode[]) new AbstractSplitPlaceholderConverter.BuildNode[ length ]; } private BuildNode get( int index ){ if( children == null ){ children = createArray( index+1 ); } if( children.length <= index ){ BuildNode[] temp = createArray( index+1 ); System.arraycopy( children, 0, temp, 0, children.length ); children = temp; } if( children[index] == null ){ children[index] = new BuildNode(); } return children[index]; } public SplitDockTree<D>.Key collapse( SplitDockTree<D> tree ){ if( "l".equals( type ) || "p".equals( type )){ if( placeholders != null && placeholders.length > 0 ){ return tree.put( placeholders, map, id ); } return null; } else if( "n".equals( type )){ SplitDockTree<D>.Key left = null; SplitDockTree<D>.Key right = null; if( children != null && children.length > 0 && children[0] != null ){ left = children[0].collapse( tree ); } if( children != null && children.length > 1 && children[1] != null ){ right = children[1].collapse( tree ); } if( left == null ){ return right; } if( right == null ){ return left; } if( orientation ){ return tree.horizontal( left, right, divider, placeholders, map, id ); } else{ return tree.vertical( left, right, divider, placeholders, map, id ); } } else if( "r".equals( type )){ if( children != null && children.length > 0 && children[0] != null ){ SplitDockTree<D>.Key child = children[0].collapse( tree ); if( child != null ){ tree.root( child ); return child; } } return null; } else{ return null; } } } }