/* * 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.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.HashMap; import java.util.List; import java.util.Map; import bibliothek.gui.DockController; import bibliothek.gui.dock.station.flap.FlapDockPropertyFactory; import bibliothek.gui.dock.station.screen.ScreenDockPropertyFactory; import bibliothek.gui.dock.station.split.SplitDockFullScreenPropertyFactory; import bibliothek.gui.dock.station.split.SplitDockPathPropertyFactory; import bibliothek.gui.dock.station.split.SplitDockPlaceholderPropertyFactory; import bibliothek.gui.dock.station.split.SplitDockPropertyFactory; import bibliothek.gui.dock.station.stack.StackDockPropertyFactory; import bibliothek.gui.dock.util.extension.ExtensionName; import bibliothek.util.Path; import bibliothek.util.Version; import bibliothek.util.xml.XElement; /** * A PropertyTransformer can read and write instances of {@link DockableProperty} * , assuming that a factory is installed for the property. * * @author Benjamin Sigg * */ public class PropertyTransformer { /** Name of the {@link ExtensionName} that allows to load additional {@link DockablePropertyFactory}s */ public static final Path FACTORY_EXTENSION = new Path( "dock.DockablePropertyFactory" ); /** Name of the only property of an {@link ExtensionName} that points to <code>this</code> */ public static final String FACTORY_EXTENSION_PARAMETER = "transformer"; private Map<String, DockablePropertyFactory> factories = new HashMap<String, DockablePropertyFactory>(); /** * Creates a new transformer, the factories for all {@link DockableProperty}s implemented * by this framework are installed. * @param controller the controller in whose realm this transformer is used */ public PropertyTransformer(DockController controller){ this( controller, SplitDockPropertyFactory.FACTORY, SplitDockPathPropertyFactory.FACTORY, SplitDockPlaceholderPropertyFactory.FACTORY, SplitDockFullScreenPropertyFactory.FACTORY, StackDockPropertyFactory.FACTORY, FlapDockPropertyFactory.FACTORY, ScreenDockPropertyFactory.FACTORY ); } /** * Creates a new transformer and installs <code>factories</code>. * @param factories a list of factories to install * @param controller the controller in whose realm this transformer is used */ public PropertyTransformer( DockController controller, DockablePropertyFactory... factories ){ for (DockablePropertyFactory factory : factories){ this.factories.put( factory.getID(), factory ); } List<DockablePropertyFactory> extensions = controller.getExtensions().load( new ExtensionName<DockablePropertyFactory>( FACTORY_EXTENSION, DockablePropertyFactory.class, FACTORY_EXTENSION_PARAMETER, this ) ); for( DockablePropertyFactory factory : extensions ){ this.factories.put( factory.getID(), factory ); } } /** * Installs a factory * @param factory the new factory */ public void addFactory( DockablePropertyFactory factory ){ factories.put( factory.getID(), factory ); } /** * Writes <code>property</code> and all its successors into <code>out</code>. * @param property the property to write * @param out a stream to write into * @throws IOException if the stream throws an exception */ public void write( DockableProperty property, DataOutputStream out ) throws IOException{ Version.write( out, Version.VERSION_1_0_4 ); int count = 0; DockableProperty successor = property; while( successor != null ) { count++; successor = successor.getSuccessor(); } out.writeInt( count ); while( property != null ) { out.writeUTF( property.getFactoryID() ); ByteArrayOutputStream bytes = new ByteArrayOutputStream(); DataOutputStream datas = new DataOutputStream( bytes ); property.store( datas ); datas.close(); byte[] written = bytes.toByteArray(); out.writeInt( written.length ); out.write( written ); property = property.getSuccessor(); } } /** * Reads a property which was earlier stored. If the property had any * successors, then they are read as well. * @param in a stream to read from * @return the properties * @throws IOException if the property can't be read */ public DockableProperty read( DataInputStream in ) throws IOException{ Version version = Version.read( in ); version.checkCurrent(); int count = in.readInt(); DockableProperty property = null; DockableProperty base = null; for (int i = 0; i < count; i++) { String id = in.readUTF(); DockablePropertyFactory factory = factories.get( id ); if (factory == null) throw new IOException( "Unknown factory-id: " + id ); DockableProperty temp = factory.createProperty(); int length = in.readInt(); byte[] data = new byte[length]; int index = 0; while( index < length ) { int read = in.read( data, index, length - index ); if (read < 0) throw new EOFException(); index += read; } DataInputStream datas = new DataInputStream( new ByteArrayInputStream( data ) ); temp.load( datas ); datas.close(); if (base == null) { base = temp; property = temp; } else { property.setSuccessor( temp ); property = temp; } } return base; } /** * Writes <code>property</code> and all its successors into * <code>element</code>. * @param property the property to write * @param element an xml element to which this method will add some children */ public void writeXML( DockableProperty property, XElement element ){ while( property != null ) { XElement xnode = element.addElement( "property" ); xnode.addString( "factory", property.getFactoryID() ); property.store( xnode ); property = property.getSuccessor(); } } /** * Reads a {@link DockableProperty} and its successors from an xml element. * @param element the element to read from * @return the property or <code>null</code> if <code>element</code> is empty * @throws IllegalArgumentException if a {@link DockablePropertyFactory} is missing. */ public DockableProperty readXML( XElement element ){ DockableProperty base = null; DockableProperty property = null; for (XElement xnode : element.getElements( "property" )) { DockablePropertyFactory factory = factories.get( xnode.getString( "factory" ) ); if (factory == null) throw new IllegalArgumentException( "Missing factory: " + xnode.getString( "factory" ) ); DockableProperty next = factory.createProperty(); next.load( xnode ); if (property == null) { property = next; base = next; } else { property.setSuccessor( next ); property = next; } } return base; } }