/* * 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 * benjamin_sigg@gmx.ch * 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<String, DockElement> stringToElement = new HashMap<String, DockElement>(); /** A mapping from a list of elements to their ids */ private Map<DockElement, String> elementToString = new HashMap<DockElement, String>(); 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<String, DockFactory<? extends DockElement,?,? extends BackupFactoryData<?>>> backups = new HashMap<String, DockFactory<? extends DockElement,?,? extends BackupFactoryData<?>>>(); private final PreloadFactory factory = new PreloadFactory(); /** * Creates a new {@link DockSituation}, uses <code>controller</code> 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.<br> * 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 <code>this</code> 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 <code>factory</code>. The * factory has only to look at the {@link BackupFactoryData#getData() data}-property. * This {@link PredefinedDockSituation} will set the identifier whenever * a method of <code>factory</code> 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 <code>id</code>. * @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.<br> * <b>Note:</b> 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 <code>element</code> itself should be stored * or loaded, that will not prevent the <code>element</code> from showing * up but from changing its content. The default implementation returns * always <code>true</code>. This method is intended to be overridden by * subclasses. * @param element the element whose contents might or might not be stored * or loaded * @return <code>true</code> if the contents should be handled, <code>false</code> * if they should be discarded */ protected boolean shouldLayout( DockElement element ){ return true; } /** * Tells whether the layout of <code>element</code> itself should be stored * or loaded, that will not prevent the <code>element</code> from showing * up but from changing its content. The default implementation uses the maps * of <code>perspective</code> and this {@link DockSituation} to convert <code>element</code> * into a preset {@link DockElement} and calls {@link #shouldLayout(DockElement)}. If the * element is not found, <code>true</code> 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 <code>true</code> if the contents should be handled, <code>false</code> * 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<PredefinedLayout>( KNOWN, new PredefinedLayout( preloaded.getPredefined(), newDelegate ))); } } return info; } /** * Given a set of <code>Dockable</code>s this method * estimates which of them will be visible once <code>composition</code> * is applied. * @param <D> the kind of elements to check * @param base a collection of <code>Dockable</code>s in no specific * order and with no restrictions * @param composition location information for various elements * @return A subset of <code>base</code> with those elements which will * be visible once this situation converts <code>composition</code> */ public <D extends DockElement> Set<D> listVisible( Collection<D> base, DockLayoutComposition composition ){ Set<D> result = new HashSet<D>(); listVisible( base, composition, result ); return result; } @SuppressWarnings("unchecked") private <D extends DockElement> void listVisible( final Collection<D> base, DockLayoutComposition composition, final Set<D> 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 <code>composition</code> its * estimated location.<br> * 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<String, DockableProperty> listEstimatedLocations( DockLayoutComposition composition, boolean missingOnly ){ return listEstimatedLocations( composition, composition.getLayout().getLocation(), missingOnly ); } /** * Lists for all keys that can be found in <code>composition</code> its * estimated location.<br> * 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 <code>composition</code> 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<String, DockableProperty> listEstimatedLocations( DockLayoutComposition composition, DockableProperty location, boolean missingOnly ){ estimateLocations( composition, location ); Map<String, DockableProperty> map = new HashMap<String, DockableProperty>(); 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<String, DockableProperty> map ){ DockableProperty location = composition.getLayout().getLocation(); if( location != null ){ String key = getKey( composition, missingOnly ); if( key != null){ map.put( key, location ); } } List<DockLayoutComposition> 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<String, DockLayoutComposition> listLayouts( DockLayoutComposition composition, boolean missingOnly ){ Map<String, DockLayoutComposition> map = new HashMap<String, DockLayoutComposition>(); listLayouts( composition, missingOnly, map ); return map; } private void listLayouts( DockLayoutComposition composition, boolean missingOnly, Map<String, DockLayoutComposition> map ){ String key = getKey( composition, missingOnly ); if( key != null){ map.put( key, composition ); } List<DockLayoutComposition> children = composition.getChildren(); if( children != null ){ for( DockLayoutComposition child : children ){ listLayouts( child, missingOnly, map ); } } } /** * Gets the name of element which is represented by <code>composition</code>. * @param composition the composition whose element key is searched * @param missingOnly if set, then the key will only be returned if <code>composition</code> * is not fully loaded * @return the key or <code>null</code> */ 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 <code>layout</code>. * @param layout the layout to read * @return either a new info or <code>null</code> 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<DockElement,?,Object> factory = (DockFactory<DockElement,?,Object>)getFactory( factoryId ); DockLayoutInfo info = null; if( factory == null ){ DockFactory<?,?,BackupFactoryData<?>> backup = getBackup( factoryId ); if( backup != null ){ BackupFactoryData<Object> data = (BackupFactoryData<Object>)backup.read( in, getPlaceholderStrategy() ); if( data != null && data.getData() != null ){ info = new DockLayoutInfo( new DockLayout<Object>( factoryId, data.getData() )); } } } else{ Object delegate = factory.read( in, getPlaceholderStrategy() ); if( delegate != null ){ info = new DockLayoutInfo( new DockLayout<Object>( factoryId, delegate )); } } in.close(); return info; } catch( IOException ex ){ throw new IllegalArgumentException( "Cannot read stream", ex ); } } /** * Tries to read the xml data in <code>layout</code>. * @param layout the layout to read * @return either a new info or <code>null</code> if the data could * not be read */ @SuppressWarnings("unchecked") private DockLayoutInfo fillMissingXML( PredefinedLayout layout ){ XElement xdelegate = layout.getDelegate().getDataXML(); String factoryId = xdelegate.getString( "id" ); Object delegate = null; DockFactory<DockElement,?,Object> factory = (DockFactory<DockElement,?,Object>)getFactory( factoryId ); if( factory == null ){ DockFactory<?,?,BackupFactoryData<?>> backup = getBackup( factoryId ); if( backup != null ){ BackupFactoryData<Object> data = (BackupFactoryData<Object>)backup.read( xdelegate, getPlaceholderStrategy() ); if( data != null ) delegate = data.getData(); } } else{ delegate = factory.read( xdelegate, getPlaceholderStrategy() ); } if( delegate == null ){ return null; } return new DockLayoutInfo( new DockLayout<Object>( factoryId, delegate ) ); } /** * Gets the identifier that matches <code>element</code> which is part of <code>perspective</code>. * @param element some element whose identifier is searched * @param perspective the owner of <code>element</code> * @return the identifier */ protected String getID( PerspectiveElement element, PredefinedPerspective perspective ){ String key = perspective.get( element ); if( key == null ) return UNKNOWN + super.getID( element ); else return KNOWN; } @Override public String getID( DockElement dockable ){ String key = elementToString.get( dockable ); if( key == null ) return UNKNOWN + super.getID( dockable ); else return KNOWN; } @Override protected String getID( DockFactory<?,?,?> factory ) { if( factory == this.factory ) return KNOWN; else return UNKNOWN + super.getID( factory ); } @Override protected String getFactoryID( String id ) { if( KNOWN.equals( id )) return factory.getID(); else return id.substring( UNKNOWN.length() ); } /** * Given the unique identifier of a factory tells what identifier will be used * internally. This method may be useful if creating a {@link DockLayout} manually. * @param id the id of some factory * @return the id that will be used internally */ public static String convertFactoryID( String id ){ return UNKNOWN + id; } @Override public DockFactory<? extends DockElement,?,?> getFactory( String id ){ if( KNOWN.equals( id )) return factory; else return super.getFactory( id ); } /** * Searches a backup factory with the name <code>id</code>. * @param id the name of the factory * @return the factory or <code>null</code> */ @SuppressWarnings( "unchecked" ) protected DockFactory<? extends DockElement,?,BackupFactoryData<?>> getBackup( String id ){ return (DockFactory<? extends DockElement,?,BackupFactoryData<?>>)backups.get( id ); } @Override public String getIdentifier( DockLayoutComposition composition ){ DockLayout<?> layout = composition.getLayout().getDataLayout(); if( layout != null && layout.getFactoryID().equals( KNOWN )){ PredefinedLayout predefined = (PredefinedLayout) layout.getData(); return predefined.getPredefined(); } return null; } /** * A factory which uses other factories as delegate. This factory does * not always use the delegates, sometimes it does just read an element * which was predefined in {@link PredefinedDockSituation}. * @author Benjamin Sigg */ private class PreloadFactory implements DockFactory<DockElement,PerspectiveElement,PredefinedLayout>{ private PredefinedPerspective perspective; /** * Creates a new factory */ public PreloadFactory(){ // nothing } /** * Creates a new factory * @param perspective the perspective whose contents should be used for handling {@link PerspectiveElement} */ public PreloadFactory( PredefinedPerspective perspective ){ this.perspective = perspective; } public String getID() { return KNOWN; } @SuppressWarnings("unchecked") public void estimateLocations( PredefinedLayout layout, LocationEstimationMap children ){ DockLayoutInfo delegate = layout.getDelegate(); if( delegate.getKind() == DockLayoutInfo.Data.DOCK_LAYOUT ){ String factoryId = delegate.getDataLayout().getFactoryID(); DockFactory<DockElement,?,Object> factory = (DockFactory<DockElement,?,Object>)getFactory( factoryId ); if( factory != null ){ factory.estimateLocations( delegate.getDataLayout().getData(), children ); } } } @SuppressWarnings("unchecked") public PredefinedLayout getLayout( DockElement element, Map<Dockable, Integer> children ) { if( shouldLayout( element )){ String factoryId = UNKNOWN + PredefinedDockSituation.super.getID( element ); DockFactory<DockElement,?,Object> factory = (DockFactory<DockElement,?,Object>)getFactory( factoryId ); if( factory == null ) throw new IllegalStateException( "Missing factory: " + factoryId ); Object data = factory.getLayout( element, children ); DockLayout<Object> layout = new DockLayout<Object>( factoryId, data ); return new PredefinedLayout( elementToString.get( element ), new DockLayoutInfo( layout )); } else{ return new PredefinedLayout( elementToString.get( element ), new DockLayoutInfo() ); } } @SuppressWarnings("unchecked") public void setLayout( DockElement element, PredefinedLayout layout, Map<Integer, Dockable> children, PlaceholderStrategy placeholders ) { DockLayoutInfo delegate = layout.getDelegate(); if( delegate.getKind() == DockLayoutInfo.Data.DOCK_LAYOUT && shouldLayout( element )){ String factoryId = delegate.getDataLayout().getFactoryID(); DockFactory<DockElement,?,Object> factory = (DockFactory<DockElement,?,Object>)getFactory( factoryId ); if( factory != null ){ DockController controller = element.getController(); try{ if( controller != null ) controller.freezeLayout(); factory.setLayout( element, delegate.getDataLayout().getData(), children, placeholders ); } finally{ if( controller != null ) controller.meltLayout(); } } } } @SuppressWarnings("unchecked") public void setLayout( DockElement element, PredefinedLayout layout, PlaceholderStrategy placeholders ) { DockLayoutInfo delegate = layout.getDelegate(); if( delegate.getKind() == DockLayoutInfo.Data.DOCK_LAYOUT && shouldLayout( element )){ String factoryId = delegate.getDataLayout().getFactoryID(); DockFactory<DockElement,?,Object> factory = (DockFactory<DockElement,?,Object>)getFactory( factoryId ); if( factory != null ){ DockController controller = element.getController(); try{ if( controller != null ) controller.freezeLayout(); factory.setLayout( element, delegate.getDataLayout().getData(), placeholders ); } finally{ if( controller != null ) controller.meltLayout(); } } } } public DockElement layout( PredefinedLayout layout, Map<Integer, Dockable> children, PlaceholderStrategy placeholders ) { DockLayoutInfo delegate = layout.getDelegate(); boolean isLayout = delegate.getKind() == DockLayoutInfo.Data.DOCK_LAYOUT; boolean isNull = delegate.getKind() == DockLayoutInfo.Data.NULL; if( !isLayout && !isNull ){ return null; } DockElement element = stringToElement.get( layout.getPredefined() ); if( element == null && isLayout ){ String factoryId = delegate.getDataLayout().getFactoryID(); DockFactory<?, ?, BackupFactoryData<?>> factory = getBackup( factoryId ); if( factory != null ){ return factory.layout( new BackupFactoryData<Object>( layout.getPredefined(), delegate.getDataLayout().getData()), children, placeholders ); } return null; } setLayout( element, layout, children, placeholders ); return element; } public DockElement layout( PredefinedLayout layout, PlaceholderStrategy placeholders ) { DockLayoutInfo delegate = layout.getDelegate(); boolean isLayout = delegate.getKind() == DockLayoutInfo.Data.DOCK_LAYOUT; boolean isNull = delegate.getKind() == DockLayoutInfo.Data.NULL; if( !isLayout && !isNull ){ return null; } DockElement element = stringToElement.get( layout.getPredefined() ); if( element == null && isLayout ){ if( layout.getDelegate() == null ) return null; String factoryId = delegate.getDataLayout().getFactoryID(); DockFactory<?, ?, BackupFactoryData<?>> factory = getBackup( factoryId ); if( factory != null ){ return factory.layout( new BackupFactoryData<Object>( layout.getPredefined(), delegate.getDataLayout().getData()), placeholders); } return null; } setLayout( element, layout, placeholders ); return element; } @SuppressWarnings("unchecked") public PerspectiveElement layoutPerspective( PredefinedLayout layout, Map<Integer, PerspectiveDockable> children ){ if( perspective == null ){ throw new IllegalStateException( "the perspective of this factory is not set, meaning this factory cannot be used handling perspective dependent tasks" ); } DockLayoutInfo delegate = layout.getDelegate(); boolean isLayout = delegate.getKind() == DockLayoutInfo.Data.DOCK_LAYOUT; boolean isNull = delegate.getKind() == DockLayoutInfo.Data.NULL; if( !isLayout && !isNull ){ return null; } PerspectiveElement element = perspective.get( layout.getPredefined() ); if( element == null && isLayout ){ String factoryId = delegate.getDataLayout().getFactoryID(); DockFactory factory = getBackup( factoryId ); if( factory != null ){ return factory.layoutPerspective(new BackupFactoryData<Object>( layout.getPredefined(), delegate.getDataLayout().getData()), children ); } return null; } layoutPerspective( element, layout, children ); return element; } @SuppressWarnings("unchecked") public void layoutPerspective( PerspectiveElement element, PredefinedLayout layout, Map<Integer, PerspectiveDockable> children ){ DockLayoutInfo delegate = layout.getDelegate(); if( delegate.getKind() == DockLayoutInfo.Data.DOCK_LAYOUT && shouldLayout( element, perspective )){ String factoryId = delegate.getDataLayout().getFactoryID(); DockFactory factory = getFactory( factoryId ); if( factory != null ){ factory.layoutPerspective( element, delegate.getDataLayout().getData(), children ); } } } @SuppressWarnings("unchecked") public PredefinedLayout getPerspectiveLayout( PerspectiveElement element, Map<PerspectiveDockable, Integer> children ){ if( perspective == null ){ throw new IllegalStateException( "the perspective of this factory is not set, meaning this factory cannot be used handling perspective dependent tasks" ); } DockLayoutInfo info; if( shouldLayout( element, perspective )){ String factoryId = UNKNOWN + PredefinedDockSituation.super.getID( element ); DockFactory factory = getFactory( factoryId ); if( factory == null ) throw new IllegalStateException( "Missing factory: " + factoryId ); Object data = factory.getPerspectiveLayout( element, children ); DockLayout<Object> layout = new DockLayout<Object>( factoryId, data ); info = new DockLayoutInfo( layout ); } else{ info = new DockLayoutInfo(); } String key = perspective.get( element ); if( key == null ){ throw new IllegalStateException( "Expected a key for an element, the element should be known to the perspective, otherwise this method would not have been called: '" + element +"'" ); } return new PredefinedLayout( key, info ); } @SuppressWarnings("unchecked") public void write( PredefinedLayout layout, DataOutputStream out ) throws IOException { Version.write( out, Version.VERSION_1_0_7 ); DockLayoutInfo info = layout.getDelegate(); out.writeUTF( layout.getPredefined() ); if( info.getKind() == DockLayoutInfo.Data.BYTE ){ out.writeBoolean( true ); out.write( info.getDataByte() ); } else if( info.getKind() == DockLayoutInfo.Data.DOCK_LAYOUT ){ out.writeBoolean( true ); DockLayout delegate = info.getDataLayout(); String factoryId = delegate.getFactoryID(); DockFactory<DockElement,?,Object> factory = (DockFactory<DockElement,?,Object>)getFactory( factoryId ); if( factory == null ) throw new IOException( "Missing factory: " + factoryId ); out.writeUTF( factoryId ); factory.write( delegate.getData(), out ); } else if( info.getKind() == DockLayoutInfo.Data.NULL ){ out.writeBoolean( false ); } else{ throw new IllegalArgumentException( "Cannot store information as byte[], it is not present as raw byte[] or in an understandable format" ); } } @SuppressWarnings("unchecked") public PredefinedLayout read( DataInputStream in, PlaceholderStrategy placeholders ) throws IOException { Version version = Version.read( in ); version.checkCurrent(); boolean version7 = Version.VERSION_1_0_7.compareTo( version ) <= 0; String preloaded = in.readUTF(); boolean nullValue = false; if( version7 ){ nullValue = !in.readBoolean(); } DockLayoutInfo info = null; if( nullValue ){ info = new DockLayoutInfo(); } else{ String factoryId = in.readUTF(); DockFactory<DockElement,?,Object> factory = (DockFactory<DockElement,?,Object>)getFactory( factoryId ); if( factory == null ){ DockFactory backup = getBackup( factoryId ); if( backup != null ){ BackupFactoryData<Object> data = (BackupFactoryData<Object>)backup.read( in, placeholders ); if( data != null && data.getData() != null ){ info = new DockLayoutInfo( new DockLayout<Object>( factoryId, data.getData() )); } } else{ // store as byte[] ByteArrayOutputStream out = new ByteArrayOutputStream(); DataOutputStream dout = new DataOutputStream( out ); dout.writeUTF( factoryId ); int read; while( (read = in.read()) != -1 ){ dout.write( read ); } dout.close(); info = new DockLayoutInfo( out.toByteArray() ); } } else{ Object delegate = factory.read( in, placeholders ); if( delegate != null ){ info = new DockLayoutInfo( new DockLayout<Object>( factoryId, delegate )); } } } if( info == null ) return null; return new PredefinedLayout( preloaded, info ); } @SuppressWarnings("unchecked") public void write( PredefinedLayout layout, XElement element ) { element.addElement( "replacement" ).addString( "id", layout.getPredefined() ); DockLayoutInfo info = layout.getDelegate(); if( info.getKind() == DockLayoutInfo.Data.XML ){ element.addElement( info.getDataXML() ); } else if( info.getKind() == DockLayoutInfo.Data.DOCK_LAYOUT ){ DockLayout<?> delegate = layout.getDelegate().getDataLayout(); String factoryId = delegate.getFactoryID(); DockFactory<DockElement,?,Object> factory = (DockFactory<DockElement,?,Object>)getFactory( factoryId ); if( factory == null ) throw new XException( "Missing factory: " + factoryId ); XElement xdelegate = element.addElement( "delegate" ); xdelegate.addString( "id", factoryId ); factory.write( delegate.getData(), xdelegate ); } else if( info.getKind() == DockLayoutInfo.Data.NULL ){ // nothing to store } else{ throw new IllegalArgumentException( "Cannot store information as xml, it is neither present as raw xml nor in an understandable format" ); } } @SuppressWarnings("unchecked") public PredefinedLayout read( XElement element, PlaceholderStrategy placeholders ) { String preload = element.getElement( "replacement" ).getString( "id" ); XElement xdelegate = element.getElement( "delegate" ); if( xdelegate == null ){ return new PredefinedLayout( preload, new DockLayoutInfo() ); } String factoryId = xdelegate.getString( "id" ); Object delegate = null; DockFactory<DockElement,?,Object> factory = (DockFactory<DockElement,?,Object>)getFactory( factoryId ); if( factory == null ){ DockFactory backup = getBackup( factoryId ); if( backup != null ){ BackupFactoryData<Object> data = (BackupFactoryData<Object>)backup.read( xdelegate, placeholders ); if( data != null ) delegate = data.getData(); } } else{ delegate = factory.read( xdelegate, placeholders ); } if( delegate == null ){ return new PredefinedLayout( preload, new DockLayoutInfo( xdelegate )); } else{ DockLayout<Object> layout = new DockLayout<Object>( factoryId, delegate ); return new PredefinedLayout( preload, new DockLayoutInfo( layout ) ); } } } }