/******************************************************************************* * Copyright (c) 2002-2006 Innoopract Informationssysteme GmbH. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Innoopract Informationssysteme GmbH - initial API and implementation ******************************************************************************/ package com.w4t; import java.lang.reflect.Field; import java.util.Vector; import org.eclipse.rwt.internal.util.ParamCheck; import com.w4t.custom.ICustomContainer; import com.w4t.dhtml.Node; import com.w4t.event.*; import com.w4t.internal.simplecomponent.UniversalAttributes; import com.w4t.util.ComponentTreeVisitor; import com.w4t.util.ComponentTreeVisitor.AllComponentVisitor; /** * <p>A WebContainer is a WebComponent, which contains other WebComponents. * WebContainers are used for grouping and positioning WebComponents.</p> * * <p>The components on WebContaines are arranged by layout managers, * which must be subclasses of {@link com.w4t.WebLayout WebLayout}. * The default WebLayout for a WebContainer is the * {@link com.w4t.WebBorderLayout WebBorderLayout}.</p> * * <p>The WebContainer implements the WebActionListener interface * in order to receive WebActionEvents, which it passes to all its added * components, and to which WebActionListeners may be added that are then * notified about every WebActionEvent that occurs on a component added to * this WebContainer.</p> */ public abstract class WebContainer extends WebComponent implements Validatable, SimpleComponent { /** the default Layout Manager of the WebContainer is the WebBorderLayout * ({@link #getWebLayout getWebLayout}, * {@link #setWebLayout setWeblayout}) */ private WebLayout webLayout = new WebBorderLayout(); /** dynamic datastructure for the webComponents, added to this WebContainer * the second vector field contains the information of the region * of the layout manager to which the component was added */ private Vector[] webComponentList = new Vector[ 2 ]; /** counter for the webComponents added to this WebContainer * {@link #getWebComponentCount getWebComponentCount} */ private int webComponentCount = 0; /** <p>whether the validation on this WebContainer is active, i.e. * whether validation is actually performed on validate() call.</p> */ private boolean validationActive = true; /** the universal html attributes encapsulation class */ private UniversalAttributes universalAttributes; /** <p>if this is an instance of ICustomContainer the layout * could only be changed once.</p>*/ private boolean hasCustomLayout = false; /** Creates a new instance of WebContainer. */ protected WebContainer() { super(); // management for added components webComponentList[ 0 ] = new Vector(); webComponentList[ 1 ] = new Vector(); } /** * <p>Returns a clone of this WebComponent.</p> * * <p>For all elementary WebComponents, cloning will result in a deep copy, * shallow copying all fields, cloning property objects (i.e. all * subclasses of WebComponentProperties, like Style, WindowProperties * etc.), and in case the WebObject to clone is a WebContainer, also * cloning all WebComponents, if they were added to it using * WebContainer.add(), and the WebLayout, if it was set using * WebContainer.setWebLayout(). Fields containing external references to * Objects which are not added with one of those methods are set to null * in the cloned object.</p> */ public Object clone() throws CloneNotSupportedException { WebContainer result = ( WebContainer )super.clone(); /* we must perform a separate init for the clone, since no constructors are called in Object.clone() */ result.webComponentList = new Vector[ 2 ]; result.webComponentList[ 0 ] = new Vector(); result.webComponentList[ 1 ] = new Vector(); result.webComponentCount = 0; result.universalAttributes = null; if( universalAttributes != null ) { result.universalAttributes = ( UniversalAttributes )universalAttributes.clone(); } result.hasCustomLayout = false; /* clone the WebLayout set on this container */ try { WebLayout layout = ( WebLayout )this.getWebLayout().clone(); result.setWebLayout( layout ); } catch ( Exception ex ) { System.err.println( "Exception occured in 'WebContainer.clone()' " + " cloning and setting the WebLayout. " + ex.toString() ); } /* clone the WebComponents added to this container */ int numAddedComponents = 0; Field[] fields = null; try { // preparations numAddedComponents = this.getWebComponentCount(); fields = getDeclaredFields(); } catch ( Exception ex ) { System.err.println( "Exception occured in 'WebContainer.clone()' " + " reading the declared fields. " + ex.toString() ); } WebComponent wcI = null; WebComponent wcIClone = null; Object wcIConstraint = null; Class webComponentClass = null; try { webComponentClass = Class.forName( "com.w4t.WebComponent" ); } catch ( ClassNotFoundException cnfe ) { System.err.println( "Exception occured in 'WebContainer.clone()' " + " creating the WebComponent Class object. " + cnfe.toString() ); } for ( int i = 0; i < numAddedComponents; i++ ) { // clone the i. added component and add it to the clone of this Container try { wcI = ( WebComponent )this.webComponentList[ 0 ].get( i ); wcIClone = ( WebComponent )wcI.clone(); wcIConstraint = this.webComponentList[ 1 ].get( i ); result.add( wcIClone, wcIConstraint ); } catch ( Exception ex ) { System.err.println( "Exception occured in 'WebContainer.clone()' " + " cloning and setting the WebLayout. " + ex.toString() ); } // if one of the fields in this WebContainer or in one of its superclasses // contains a reference to the added component, this structure is // duplicated (the clone's fields is set to the corresponding WebComponent // added to the clone int length = fields.length; for ( int j = 0; j < length; j++ ) { try { if ( webComponentClass.isAssignableFrom( fields[ j ].getType() ) ) { fields[ j ].setAccessible( true ); if ( ( WebComponent )fields[ j ].get( this ) == wcI ) { // set the corresponding field in clone fields[ j ].set( result, wcIClone ); } } } catch ( Exception ex ) { // IllegalArgumentException // IllegalAccessException System.err.println( "Exception occured in 'WebContainer.clone()' " + " duplicating the fields reference structure. " + ex.toString() ); } } // for fields } // for added components return result; } /** * <p>Adds the specified WebComponent to the WebContainer.</p> * * @param webComponent the WebComponent to be added */ public final void add( final WebComponent webComponent ) { add( webComponent, null ); } /** * <p>Adds the specified WebComponent to the WebContainer, using * the specified constraint object.</p> * * @param componentToAdd the WebComponent to be added * @param constraints where/how the component is added to the WebLayout. */ public final void add( final WebComponent componentToAdd, final Object constraints ) { ParamCheck.notNull( componentToAdd, "componentToAdd" ); checkConstraints( constraints ); checkIfAlreadyAdded( componentToAdd ); checkIfComponentIsForm( componentToAdd ); checkSelfContainment( componentToAdd ); webComponentList[ 0 ].add( componentToAdd ); webComponentList[ 1 ].add( constraints ); webComponentCount++; componentToAdd.parent = this; // check if the webComponent is a Decorator updateDecoratorOnAdd( componentToAdd ); // notify the container listener about the adding int evtId = WebContainerEvent.COMPONENT_ADDED; WebContainerEvent wcEvt = new WebContainerEvent( this, evtId, componentToAdd, constraints ); wcEvt.processEvent(); } private void checkSelfContainment( final WebComponent componentToAdd ) { if( componentToAdd instanceof WebContainer ) { for( WebContainer wc = this; wc != null; wc = wc.getParent() ) { if( wc == componentToAdd ) { String msg = "Error in 'WebContainer.add()': " + "adding container's parent to itself."; throw new IllegalArgumentException( msg ); } } } } private void checkIfComponentIsForm( final WebComponent componentToAdd ) { if( componentToAdd instanceof WebForm ) { String msg = "A instance of WebForm cannot be added to a WebContainer!"; String compName = componentToAdd.getName(); if ( !"".equals( compName ) ) { msg += "\nName of the WebForm is '" + compName + "'."; } throw new IllegalArgumentException( msg ); } } private void checkIfAlreadyAdded( final WebComponent componentToAdd ) { if( componentToAdd.getParent() != null ) { String msg = "This WebComponent '" + componentToAdd.getClass().getName() + "' is already added to a WebContainer!"; throw new IllegalArgumentException( msg ); } } private void checkConstraints( final Object constraints ) { if( !webLayout.checkConstraint( constraints ) ) { String constraintsType = constraints == null ? "null" : constraints.getClass().getName(); String msg = "Wrong constraint type: '" + constraintsType + "'.\n" + "Use a constraint object of the '" + webLayout.getClass().getName()+ "'"; throw new IllegalArgumentException( msg ); } } private void updateDecoratorOnAdd( final WebComponent webComponent ) { if( webComponent instanceof Decorator && ( ( Decorator )webComponent ).getContent() != null ) { Decorator decorator = ( Decorator )webComponent; decorator.getContent().parent = this; updateDecoratorOnAdd( decorator.getContent() ); } } /** * Removes the specified WebComponent from the WebContainer. * * @param toRemove the WebComponent to removed */ public void remove( final WebComponent toRemove ) { Object constraint = null; WebComponent child = Decorator.isDecorated( toRemove ) ? Decorator.getOuterMostDecorator( toRemove ) : toRemove; if( webComponentList[ 0 ].contains( child ) ) { boolean removed = false; if( child instanceof Decorator ) { updateDecoratorOnRemove( ( Decorator )child, toRemove ); } if( child instanceof Node ) { updateNodeOnRemove( ( Node )child ); } for( int i = 0; !removed && i < webComponentCount; i++ ) { WebComponent wc = ( WebComponent )webComponentList[ 0 ].get( i ); if( child == wc ) { webComponentList[ 0 ].remove( i ); constraint = webComponentList[ 1 ].remove( i ); webComponentCount--; removed = true; } } if( removed ) { toRemove.parent = null; // notify the container listener about the removing int evtId = WebContainerEvent.COMPONENT_REMOVED; WebContainerEvent wcEvt = new WebContainerEvent( this, evtId, child, constraint ); wcEvt.processEvent(); } } } private void updateNodeOnRemove( final Node node ) { ComponentTreeVisitor visitor = new AllComponentVisitor() { public boolean doVisit( final WebComponent component ) { component.parent = null; return true; } }; ComponentTreeVisitor.accept( node, visitor ); } private void updateDecoratorOnRemove( final Decorator decorator, final WebComponent toRemove ) { if( decorator.getContent() instanceof Decorator ) { Decorator contentDecorator = ( Decorator )decorator.getContent(); updateDecoratorOnRemove( contentDecorator, toRemove ); } if( decorator != toRemove ) { decorator.removeContent(); } decorator.parent = null; if( decorator.getContent() != null ) { decorator.getContent().parent = null; } } /** * Removes the specified WebComponent from the WebContainer. * * @param index the index of the WebComponent to remove */ public void remove( final int index ) { remove( this.get( index ) ); } /** * Removes all WebComponents from the WebContainer. */ public void removeAll() { for( int i = 0; i < webComponentCount; i++ ) { WebComponent wc = ( WebComponent )webComponentList[ 0 ].get( i ); wc.parent = null; } webComponentList[ 0 ].removeAllElements(); webComponentList[ 1 ].removeAllElements(); webComponentCount = 0; } /** * Returns the WebComponent at the specified index in the container * (index starts with 0). */ public WebComponent get( final int index ) { return ( WebComponent )webComponentList[ 0 ].get( index ); } /** * Returns all WebComponents that are added to this WebContainer. */ public WebComponent[] get() { WebComponent[] result = new WebComponent[ getWebComponentCount() ]; webComponentList[ 0 ].toArray( result ); return result; } /** * Returns the constraints at the specified index in the container * (index starts with 0). */ public Object getConstraint( final int index ) { return webComponentList[ 1 ].get( index ); } /** * Returns the constraint object belonging to the specified component. * * @return null, if the specified WebComponent is not added to the container * of the component was added without a constraint, the depending * constraint object otherwise. */ public Object getConstraint( final WebComponent wc ) { int size = this.getWebComponentCount(); int searchIndex = -1; Object constraint = null; boolean found = false; for( int i = 0; !found && i < size; i++ ) { if( this.get( i ) == wc ) { searchIndex = i; found = true; } } if( searchIndex != -1 ) { constraint = this.getConstraint( searchIndex ); } return constraint; } /** * Returns the current WebLayout of this WebContainer. */ public final WebLayout getWebLayout() { return webLayout; } /** * Sets a new WebLayout for this WebContainer. * @param newLayout instance of the new WebLayout for this WebContainer */ public final void setWebLayout( final WebLayout newLayout ) { if( webLayout != newLayout && !hasCustomLayout ) { this.webLayout = newLayout; // notify the WebContainerListener about the change of the layout int evtId = WebContainerEvent.LAYOUT_CHANGED; WebContainerEvent wcEvt = new WebContainerEvent( this, evtId, webLayout ); wcEvt.processEvent(); } if( this instanceof ICustomContainer ) { hasCustomLayout = true; } } /** * Returns the counter of the WebComponents added to this WebContainer. */ public int getWebComponentCount() { return webComponentCount; } /** * Sets the "value" of the "designTime" property to the WebContainer and * it's added WebComponents. * * @param designTime sets the current "value" of the "designTime" property. */ public void setDesignTime( final boolean designTime ) { super.setDesignTime( designTime ); for( int i = 0; i < webComponentCount; i++ ) { WebComponent wc = ( WebComponent )webComponentList[ 0 ].get( i ); wc.setDesignTime( this.designTime ); } } /** * A value of true denotes that this WebContainer and it's added * WebComponents should behave in design time * mode, a value of false denotes runtime behavior. * * @return the current "value" of the "designTime" property. */ public boolean isDesignTime() { return designTime; } /** * <p>Adds the specified WebContainerListener to receive container events * from this WebContainer.</p> * <p>Container events occur when a WebComponent is added to or removed * from this WebContainer, or when a new WebLayout is set to this * WebContainer.</p> * * @param listener the WebContainerListener */ public void addWebContainerListener( final WebContainerListener listener ) { WebContainerEvent.addListener( this, listener ); } /** * <p>Removes the specified WebContainerListener which receives container * events from this WebContainer.</p> * <p>Container events occur when a WebComponent is added to or removed * from this WebContainer, or when a new WebLayout is set to this * WebContainer.</p> * * @param listener the WebContainerListener */ public void removeWebContainerListener( final WebContainerListener listener ){ WebContainerEvent.removeListener( this, listener ); } // interface methods of com.w4t.Validatable /////////////////////////////////////////////////////////////// /** * <p>Adds the specified listener for ValidationEvents on this * WebContainer.</p> * * <p>The {@link #validate() validate()} method is called on any * {@link Validatable Validatable}s added to this WebContainer and the * added ValidationListener is notified about the result of the * validation.</p> */ public void addValidationListener( final ValidationListener listener ) { ValidationEvent.addListener( this, listener ); } /** * <p>Removes the specified listener for ValidationEvents on this * WebContainer.</p> */ public void removeValidationListener( final ValidationListener listener ) { ValidationEvent.removeListener( this, listener ); } /** * Validates all Validatables added to this WebContainer and notifies the * ValidationListeners added to this WebContainer. * * @return the result of the validation on all Validatables added to * this WebContainer */ public boolean validate() { boolean result = true; if( validationActive ) { WebComponent[] comps = get(); for( int i = 0; i < comps.length; i++ ) { if( comps[ i ] instanceof Validatable ) { result &= ( ( Validatable )comps[ i ] ).validate(); } } int evtId = ValidationEvent.VALIDATED; ValidationEvent evt = new ValidationEvent( this, evtId, result ); evt.processEvent(); } return result; } public void setValidationActive( final boolean validationActive ) { this.validationActive = validationActive; } public boolean isValidationActive() { return validationActive; } // interface methods of com.w4t.SimpleComponent /////////////////////////////////////////////////////////////// public String getCssClass() { return getUniversalAttributes().getCssClass(); } public String getDir() { return getUniversalAttributes().getDir(); } public String getLang() { return getUniversalAttributes().getLang(); } public Style getStyle() { return getUniversalAttributes().getStyle(); } public String getTitle() { return getUniversalAttributes().getTitle(); } public void setCssClass( final String cssClass ) { getUniversalAttributes().setCssClass( cssClass ); } public void setDir( final String dir ) { getUniversalAttributes().setDir( dir ); } public void setLang( final String lang ) { getUniversalAttributes().setLang( lang ); } public void setStyle( final Style style ) { getUniversalAttributes().setStyle( style ); } public void setTitle( final String title ) { getUniversalAttributes().setTitle( title ); } public void setIgnoreLocalStyle( final boolean ignoreLocalStyle ) { getUniversalAttributes().setIgnoreLocalStyle( ignoreLocalStyle ); } public boolean isIgnoreLocalStyle() { return getUniversalAttributes().isIgnoreLocalStyle(); } UniversalAttributes getUniversalAttributes() { if( universalAttributes == null ) { universalAttributes = createUniversalAttributes(); } return universalAttributes; } private static UniversalAttributes createUniversalAttributes() { UniversalAttributes result = new UniversalAttributes(); result.getStyle().setFontFamily( "" ); result.getStyle().setFontSize( Style.NOT_USED ); return result; } }