All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.metawidget.gwt.client.ui.GwtMetawidget Maven / Gradle / Ivy

There is a newer version: 4.2
Show newest version
// Metawidget (licensed under LGPL)
//
// 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package org.metawidget.gwt.client.ui;

import static org.metawidget.inspector.InspectionResultConstants.*;
import static org.metawidget.util.simple.StringUtils.*;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Set;

import org.metawidget.gwt.client.ui.layout.FlexTableLayout;
import org.metawidget.gwt.client.ui.layout.LabelLayoutDecorator;
import org.metawidget.gwt.client.ui.layout.LabelLayoutDecoratorConfig;
import org.metawidget.gwt.client.widgetbuilder.GwtWidgetBuilder;
import org.metawidget.gwt.client.widgetbuilder.OverriddenWidgetBuilder;
import org.metawidget.gwt.client.widgetbuilder.ReadOnlyWidgetBuilder;
import org.metawidget.gwt.client.widgetprocessor.StyleNameProcessor;
import org.metawidget.inspectionresultprocessor.iface.InspectionResultProcessor;
import org.metawidget.inspector.gwt.remote.client.GwtRemoteInspectorProxy;
import org.metawidget.inspector.iface.Inspector;
import org.metawidget.layout.iface.Layout;
import org.metawidget.util.simple.PathUtils;
import org.metawidget.util.simple.PathUtils.TypeAndNames;
import org.metawidget.util.simple.StringUtils;
import org.metawidget.widgetbuilder.composite.CompositeWidgetBuilder;
import org.metawidget.widgetbuilder.composite.CompositeWidgetBuilderConfig;
import org.metawidget.widgetbuilder.iface.WidgetBuilder;
import org.metawidget.widgetprocessor.iface.WidgetProcessor;

import com.google.gwt.i18n.client.Dictionary;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HasName;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.xml.client.Element;

/**
 * Metawidget for GWT environments.
 * 

* GWT compiles Java to JavaScript, and JavaScript lacks Java's comprehensive reflection support. * The only viable Inspector the JavaScript could run would be XmlInspector, and even that would * have to be considerably rewritten as GWT supplies its own variant of org.w3c.dom. *

* A more interesting solution is to have the JavaScript client send its objects (via AJAX) to the * Java server for inspection. The full power of Java Inspectors can then be brought to bear, * including inspecting annotations and server-side configuration files (such as * hibernate.cfg.xml). * * @author Richard Kennard */ public class GwtMetawidget extends FlowPanel implements HasName { // // Private statics // private static final int BUILDING_COMPLETE = 0; private static final int BUILDING_IN_PROGRESS = 1; private static final int BUILDING_NEEDED = 2; /** * Delay before rebuilding widgets (in milliseconds). *

* GWT does not define a good 'paint' method to override, so we must call * buildWidgets after every invalidateWidgets. Many methods (eg. most * setters) trigger invalidateWidgets, however, so we impose a short delay to try * and 'batch' multiple buildWidgets requests (and their associated AJAX calls) * into one. */ private static final int BUILD_DELAY = 50; /** * Static cache of the default Inspector. *

* Note this needn't be synchronized because JavaScript is not multi-threaded. *

* GWTMetawidget cannot use our ConfigReader, because that relies * heavily on reflection which is not available client-side. Note * GwtRemoteInspectorProxy does use ConfigReader. */ private static Inspector DEFAULT_INSPECTOR; private static WidgetBuilder DEFAULT_WIDGETBUILDER; private static WidgetProcessor DEFAULT_WIDGETPROCESSOR; private static Layout DEFAULT_LAYOUT; // // Private members // private Object mToInspect; private String mDictionaryName; private Dictionary mDictionary; private Map mFacets = new HashMap(); private Set mExistingWidgets = new HashSet(); private Set mExistingUnusedWidgets = new HashSet(); /** * Map of widgets added to this Metawidget. *

* Searching for Widgets by traversing children is complicated in GWT, because not all Widgets * that contain child Widgets extend a common base class. For example, both ComplexPanel and * FlexTable can contain child Widgets but have very different APIs. It is easier to keep a * separate Map of the widgets we have encountered. */ private Map mAddedWidgets = new HashMap(); private Timer mBuildWidgets; private Map mClientProperties; // // Package-private members // /* package private */String mPath; /** * Name used to implement HasName. Subtly different from mPath and * mNamesPrefix. */ /* package private */String mName; /* package private */int mNeedToBuildWidgets; /* package private */Element mLastInspection; /* package private */boolean mIgnoreAddRemove; /** * For unit tests. */ /* package private */Timer mExecuteAfterBuildWidgets; /* package private */Pipeline mPipeline; // // Constructor // public GwtMetawidget() { mPipeline = newPipeline(); } // // Public methods // /** * Gets the object being inspected. *

* Exposed for binding implementations. * * @return the object. Note this return type uses generics, so as to not require a cast by the * caller (eg. Person p = getToInspect()) */ @SuppressWarnings( "unchecked" ) public T getToInspect() { return (T) mToInspect; } /** * Sets the Object to inspect. *

* If setPath has not been set, or points to a previous setToInspect, * sets it to point to the given Object. */ public void setToInspect( Object toInspect ) { updateToInspectWithoutInvalidate( toInspect ); invalidateInspection(); } /** * Updates the Object to inspect, without invalidating the previous inspection results. *

* This is an internal API exposed for WidgetProcessor rebinding support. Clients should * not call it directly. */ public void updateToInspectWithoutInvalidate( Object toInspect ) { if ( mToInspect == null ) { if ( mPath == null && toInspect != null ) { mPath = toInspect.getClass().getName(); } } else if ( mToInspect.getClass().getName().equals( mPath ) ) { if ( toInspect == null ) { mPath = null; } else { mPath = toInspect.getClass().getName(); } } mToInspect = toInspect; } /** * Sets the path to be inspected. *

* Note setPath is quite different to * com.google.gwt.user.client.ui.HasName.setName. setPath is always in * relation to setToInspect, so must include the type name and any subsequent * sub-names (eg. type/name/name). Conversely, setName is a single name relative to * our immediate parent. setName is only implemented so that * GwtMetawidgets can be used directly as overridden widgets (i.e. without needing * to be wrapped in a Stub). */ public void setPath( String path ) { mPath = path; invalidateInspection(); } public String getPath() { return mPath; } /** * Implements the com.google.gwt.user.client.ui.HasName interface. *

* Useful so that GwtMetawidgets can be used directly as overridden widgets (i.e. * without needing to be wrapped in a Stub). Unlike setPath, has no * effect on inspection. */ public void setName( String name ) { mName = name; } /** * Implements the com.google.gwt.user.client.ui.HasName interface. *

* Useful so that GwtMetawidgets can be used directly as overridden widgets (i.e. * without needing to be wrapped in a Stub). Unlike setPath, has no * effect on inspection. */ public String getName() { return mName; } public void setInspector( Inspector inspector ) { mPipeline.setInspector( inspector ); invalidateInspection(); } /** * Useful for WidgetBuilders to perform nested inspections (eg. for Collections). */ public String inspect( Object toInspect, String type, String... names ) { return mPipeline.inspect( toInspect, type, names ); } public void addInspectionResultProcessor( InspectionResultProcessor inspectionResultProcessor ) { mPipeline.addInspectionResultProcessor( inspectionResultProcessor ); invalidateInspection(); } public void setWidgetBuilder( WidgetBuilder widgetBuilder ) { mPipeline.setWidgetBuilder( widgetBuilder ); invalidateWidgets(); } public void addWidgetProcessor( WidgetProcessor widgetProcessor ) { mPipeline.addWidgetProcessor( widgetProcessor ); invalidateWidgets(); } public T getWidgetProcessor( Class widgetProcessorClass ) { return mPipeline.getWidgetProcessor( widgetProcessorClass ); } public void setLayout( Layout layout ) { mPipeline.setLayout( layout ); invalidateWidgets(); } /** * Set the Dictionary name for localization. *

* The Dictionary name must be a JavaScript variable declared in the host HTML page. */ public void setDictionaryName( String dictionaryName ) { mDictionaryName = dictionaryName; mDictionary = null; invalidateWidgets(); } public String getLabelString( Map attributes ) { if ( attributes == null ) { return ""; } // Explicit label String label = attributes.get( LABEL ); if ( label != null ) { // (may be forced blank) if ( "".equals( label ) ) { return null; } // (localize if possible) String localized = getLocalizedKey( StringUtils.camelCase( label ) ); if ( localized != null ) { return localized.trim(); } return label.trim(); } // Default name String name = attributes.get( NAME ); if ( name != null ) { // (localize if possible) String localized = getLocalizedKey( name ); if ( localized != null ) { return localized.trim(); } return StringUtils.uncamelCase( name ); } return ""; } /** * @return null if no bundle, ???key??? if bundle is missing a key */ public String getLocalizedKey( String key ) { if ( mDictionaryName == null ) { return null; } try { if ( mDictionary == null ) { mDictionary = Dictionary.getDictionary( mDictionaryName ); } return mDictionary.get( key ); } catch ( MissingResourceException e ) { return StringUtils.RESOURCE_KEY_NOT_FOUND_PREFIX + key + StringUtils.RESOURCE_KEY_NOT_FOUND_SUFFIX; } } public void setReadOnly( boolean readOnly ) { if ( mPipeline.isReadOnly() == readOnly ) { return; } mPipeline.setReadOnly( readOnly ); invalidateWidgets(); } public boolean isReadOnly() { return mPipeline.isReadOnly(); } public int getMaximumInspectionDepth() { return mPipeline.getMaximumInspectionDepth(); } public void setMaximumInspectionDepth( int maximumInspectionDepth ) { mPipeline.setMaximumInspectionDepth( maximumInspectionDepth ); invalidateWidgets(); } /** * Gets the widget with the given name. */ @SuppressWarnings( "unchecked" ) public T getWidget( String... names ) { if ( names == null ) { return null; } if ( mNeedToBuildWidgets != BUILDING_COMPLETE ) { throw new RuntimeException( "Widgets still building asynchronously: need to complete before calling getWidget( \"" + GwtUtils.toString( names, SEPARATOR_DOT_CHAR ) + "\" )" ); } Map children = mAddedWidgets; for ( int loop = 0, length = names.length; loop < length; loop++ ) { if ( children == null ) { return null; } String name = names[loop]; Widget widget = children.get( name ); if ( widget == null ) { return null; } if ( loop == length - 1 ) { return (T) widget; } if ( !( widget instanceof GwtMetawidget ) ) { return null; } children = ( (GwtMetawidget) widget ).mAddedWidgets; } return null; } /** * Gets the value from the Widget with the given name. *

* The value is returned as it is stored in the Widget (eg. String for TextBox) so may need some * conversion before being reapplied to the object being inspected. This obviously requires * knowledge of which Widget GwtMetawidget created, which is not ideal, so clients may prefer to * use binding instead. * * @return the value. Note this return type uses generics, so as to not require a cast by the * caller (eg. String s = getValue(names)) */ @SuppressWarnings( "unchecked" ) public T getValue( String... names ) { Widget widget = getWidget( names ); if ( widget == null ) { throw new RuntimeException( "No such widget " + GwtUtils.toString( names, SEPARATOR_DOT_CHAR ) ); } return (T) getValue( widget ); } /** * Gets the value from the given Widget. * * @return the value. Note this return type uses generics, so as to not require a cast by the * caller (eg. String s = getValue(widget)) */ @SuppressWarnings( "unchecked" ) public T getValue( Widget widget ) { return (T) getValue( widget, mPipeline.getWidgetBuilder() ); } /** * Sets the Widget with the given name to the specified value. *

* Clients must ensure the value is of the correct type to suit the Widget (eg. String for * TextBox). This obviously requires knowledge of which Widget GwtMetawidget created, which is * not ideal, so clients may prefer to use binding instead. */ public void setValue( Object value, String... names ) { Widget widget = getWidget( names ); if ( widget == null ) { throw new RuntimeException( "No such widget " + GwtUtils.toString( names, SEPARATOR_DOT_CHAR ) ); } setValue( value, widget ); } /** * Sets the given Widget to the specified value. */ public void setValue( Object value, Widget widget ) { if ( !setValue( value, widget, mPipeline.getWidgetBuilder() ) ) { throw new RuntimeException( "Don't know how to setValue of a " + widget.getClass().getName() ); } } public Facet getFacet( String name ) { return mFacets.get( name ); } /** * Storage area for WidgetProcessors, Layouts, and other stateless clients. */ public void putClientProperty( Object key, Object value ) { if ( mClientProperties == null ) { mClientProperties = new HashMap(); } mClientProperties.put( key, value ); } /** * Storage area for WidgetProcessors, Layouts, and other stateless clients. */ @SuppressWarnings( "unchecked" ) public T getClientProperty( Object key ) { if ( mClientProperties == null ) { return null; } return (T) mClientProperties.get( key ); } @Override public boolean remove( int index ) { if ( !mIgnoreAddRemove ) { invalidateWidgets(); Widget widget = getChildren().get( index ); if ( widget instanceof Facet ) { mFacets.remove( ( (Facet) widget ).getName() ); } else { mExistingWidgets.remove( widget ); } } return super.remove( index ); } @Override public boolean remove( Widget widget ) { if ( !mIgnoreAddRemove ) { invalidateWidgets(); if ( widget instanceof Facet ) { mFacets.remove( ( (Facet) widget ).getName() ); } else { mExistingWidgets.remove( widget ); } } return super.remove( widget ); } @Override public void clear() { super.clear(); if ( !mIgnoreAddRemove ) { invalidateWidgets(); mFacets.clear(); mExistingWidgets.clear(); } } /** * Fetch a list of Widgets that were added manually, and have so far not been used. *

* This is an internal API exposed for OverriddenWidgetBuilder. Clients should not call * it directly. */ public Set fetchExistingUnusedWidgets() { return mExistingUnusedWidgets; } // // Protected methods // /** * Instantiate the Pipeline used by this Metawidget. *

* Subclasses wishing to use their own Pipeline should override this method to instantiate their * version. */ protected Pipeline newPipeline() { return new Pipeline(); } protected void configure() { // Sensible defaults // // We cannot use ConfigReader, because GWT's client-side JavaScript is not up to it if ( mPipeline.getInspector() == null ) { if ( DEFAULT_INSPECTOR == null ) { DEFAULT_INSPECTOR = new GwtRemoteInspectorProxy(); } mPipeline.setInspector( DEFAULT_INSPECTOR ); } if ( mPipeline.getWidgetBuilder() == null ) { if ( DEFAULT_WIDGETBUILDER == null ) { @SuppressWarnings( "unchecked" ) CompositeWidgetBuilderConfig config = new CompositeWidgetBuilderConfig().setWidgetBuilders( new OverriddenWidgetBuilder(), new ReadOnlyWidgetBuilder(), new GwtWidgetBuilder() ); DEFAULT_WIDGETBUILDER = new CompositeWidgetBuilder( config ); } mPipeline.setWidgetBuilder( DEFAULT_WIDGETBUILDER ); } if ( mPipeline.getWidgetProcessors() == null ) { if ( DEFAULT_WIDGETPROCESSOR == null ) { DEFAULT_WIDGETPROCESSOR = new StyleNameProcessor(); } mPipeline.addWidgetProcessor( DEFAULT_WIDGETPROCESSOR ); } if ( mPipeline.getLayout() == null ) { if ( DEFAULT_LAYOUT == null ) { DEFAULT_LAYOUT = new LabelLayoutDecorator( new LabelLayoutDecoratorConfig().setLayout( new FlexTableLayout() ) ); } mPipeline.setLayout( DEFAULT_LAYOUT ); } } @Override protected void add( Widget child, com.google.gwt.user.client.Element container ) { if ( !mIgnoreAddRemove ) { invalidateWidgets(); if ( child instanceof Facet ) { Facet facet = (Facet) child; mFacets.put( facet.getName(), facet ); } else { mExistingWidgets.add( child ); } // Because of the lag between invalidateWidgets() and buildWidgets(), and // because some CSS styles aren't applied until buildWidgets(), we // see a visual 'glitch' when adding new widgets (like buttons). To stop // this, we don't call super.add directly when !mIgnoreAddRemove return; } super.add( child, container ); } @Override protected void insert( Widget child, com.google.gwt.user.client.Element container, int beforeIndex, boolean domInsert ) { if ( !mIgnoreAddRemove ) { invalidateWidgets(); if ( child instanceof Facet ) { Facet facet = (Facet) child; mFacets.put( facet.getName(), facet ); } else { mExistingWidgets.add( child ); } // Because of the lag between invalidateWidgets() and buildWidgets(), and // because some CSS styles aren't applied until buildWidgets(), we // see a visual 'glitch' when inserting new widgets (like buttons). To stop // this, we don't call super.insert directly when !mIgnoreAddRemove return; } super.insert( child, container, beforeIndex, domInsert ); } /** * Invalidates the current inspection result (if any) and invalidates the widgets. *

* As an optimisation we only invalidate the widgets, not the entire inspection result, for some * operations (such as adding/removing stubs, changing read-only etc.) */ protected void invalidateInspection() { mLastInspection = null; invalidateWidgets(); } /** * Invalidates the widgets. *

* If the widgets are already invalidated, but rebuilding is not yet in progress, cancels the * pending rebuild and resets the timer. This tries to 'batch' multiple invalidate requests into * one. */ protected void invalidateWidgets() { // If widgets are already invalidated... if ( mNeedToBuildWidgets == BUILDING_NEEDED ) { // ...cancel the pending rebuild... mBuildWidgets.cancel(); } else { mNeedToBuildWidgets = BUILDING_NEEDED; // ...otherwise, clear the widgets super.clear(); mAddedWidgets.clear(); } // Schedule a new build mBuildWidgets = new Timer() { @Override public void run() { buildWidgets(); } }; mBuildWidgets.schedule( BUILD_DELAY ); } /** * Builds the widgets. *

* Unlike buildWidgets in other Metawidget implementations, this method may be * asynchronous. If the GwtMetawidget is using an GwtInspectorAsync * Inspector (which it does by default), clients should not expect the widgets to be built by * the time this method returns. */ protected void buildWidgets() { // No need to build? if ( mNeedToBuildWidgets != BUILDING_NEEDED ) { // For unit tests: if buildWidgets is already underway, rely on // mExecuteAfterBuildWidgets being injected into it. This is preferrable to running // buildWidgets() twice without calling invalidateWidgets() if ( mNeedToBuildWidgets == BUILDING_COMPLETE && mExecuteAfterBuildWidgets != null ) { Timer executeAfterBuildWidgets = mExecuteAfterBuildWidgets; mExecuteAfterBuildWidgets = null; executeAfterBuildWidgets.run(); } return; } mNeedToBuildWidgets = BUILDING_IN_PROGRESS; mPipeline.configure(); if ( mToInspect != null ) { Inspector inspector = mPipeline.getInspector(); if ( mLastInspection == null ) { // Special support for GwtRemoteInspectorProxy if ( inspector instanceof GwtRemoteInspectorProxy ) { TypeAndNames typeAndNames = PathUtils.parsePath( mPath ); ( (GwtRemoteInspectorProxy) inspector ).inspect( mToInspect, typeAndNames.getType(), typeAndNames.getNamesAsArray(), new AsyncCallback() { public void onFailure( Throwable caught ) { GwtUtils.alert( caught ); mNeedToBuildWidgets = BUILDING_COMPLETE; } public void onSuccess( String inspectionResult ) { mLastInspection = mPipeline.stringToElement( inspectionResult ); try { mIgnoreAddRemove = true; mPipeline.buildWidgets( mLastInspection ); } catch ( Exception e ) { GwtUtils.alert( e ); } finally { mIgnoreAddRemove = false; } mNeedToBuildWidgets = BUILDING_COMPLETE; // For unit tests if ( mExecuteAfterBuildWidgets != null ) { Timer executeAfterBuildWidgets = mExecuteAfterBuildWidgets; mExecuteAfterBuildWidgets = null; executeAfterBuildWidgets.run(); } } } ); return; } } // Regular GwtInspectors try { mIgnoreAddRemove = true; if ( mLastInspection == null ) { TypeAndNames typeAndNames = PathUtils.parsePath( mPath ); mLastInspection = mPipeline.inspectAsDom( mToInspect, typeAndNames.getType(), typeAndNames.getNamesAsArray() ); } mPipeline.buildWidgets( mLastInspection ); } catch ( Exception e ) { GwtUtils.alert( e ); } finally { mIgnoreAddRemove = false; } mNeedToBuildWidgets = BUILDING_COMPLETE; // For unit tests if ( mExecuteAfterBuildWidgets != null ) { mExecuteAfterBuildWidgets.run(); mExecuteAfterBuildWidgets = null; } } } protected void startBuild() { mExistingUnusedWidgets = new HashSet( mExistingWidgets ); } /** * @param elementName * XML node name of the business field. Typically 'entity', 'property' or 'action'. * Never null */ protected void layoutWidget( Widget widget, String elementName, Map attributes ) { String name = attributes.get( NAME ); mAddedWidgets.put( name, widget ); } /** * Hook so subclasses can change which class gets created. */ protected GwtMetawidget buildNestedMetawidget() { return new GwtMetawidget(); } protected void initNestedMetawidget( GwtMetawidget nestedMetawidget, Map attributes ) throws Exception { mPipeline.initNestedPipeline( nestedMetawidget.mPipeline, attributes ); nestedMetawidget.setPath( mPath + StringUtils.SEPARATOR_FORWARD_SLASH_CHAR + attributes.get( NAME ) ); nestedMetawidget.setDictionaryName( mDictionaryName ); nestedMetawidget.setToInspect( mToInspect ); } protected void endBuild() { if ( mExistingUnusedWidgets != null ) { Layout layout = mPipeline.getLayout(); for ( Widget widgetExisting : mExistingUnusedWidgets ) { Map miscAttributes = new HashMap(); // Manually created components default to no section miscAttributes.put( SECTION, "" ); if ( widgetExisting instanceof Stub ) { Map stubAttributes = ( (Stub) widgetExisting ).getAttributes(); if ( stubAttributes != null ) { miscAttributes.putAll( stubAttributes ); } } layout.layoutWidget( widgetExisting, PROPERTY, miscAttributes, this, this ); } } } // // Private members // private Object getValue( Widget widget, WidgetBuilder widgetBuilder ) { // Recurse into CompositeWidgetBuilders if ( widgetBuilder instanceof CompositeWidgetBuilder ) { for ( WidgetBuilder widgetBuilderChild : ( (CompositeWidgetBuilder) widgetBuilder ).getWidgetBuilders() ) { Object value = getValue( widget, widgetBuilderChild ); if ( value != null ) { return value; } } return null; } // Interrogate GwtValueAccessors if ( widgetBuilder instanceof GwtValueAccessor ) { return ( (GwtValueAccessor) widgetBuilder ).getValue( widget ); } return null; } private boolean setValue( Object value, Widget widget, WidgetBuilder widgetBuilder ) { // Recurse into CompositeWidgetBuilders if ( widgetBuilder instanceof CompositeWidgetBuilder ) { for ( WidgetBuilder widgetBuilderChild : ( (CompositeWidgetBuilder) widgetBuilder ).getWidgetBuilders() ) { if ( setValue( value, widget, widgetBuilderChild ) ) { return true; } } return false; } // Interrogate GwtValueAccessors if ( widgetBuilder instanceof GwtValueAccessor ) { return ( (GwtValueAccessor) widgetBuilder ).setValue( widget, value ); } return false; } // // Inner class // protected class Pipeline extends GwtPipeline { // // Protected methods // @Override protected void configure() { GwtMetawidget.this.configure(); } @Override protected void startBuild() { GwtMetawidget.this.startBuild(); super.startBuild(); } @Override protected GwtMetawidget buildNestedMetawidget( Map attributes ) throws Exception { GwtMetawidget nestedMetawidget = GwtMetawidget.this.buildNestedMetawidget(); GwtMetawidget.this.initNestedMetawidget( nestedMetawidget, attributes ); return nestedMetawidget; } @Override protected Map getAdditionalAttributes( Widget widget ) { if ( widget instanceof Stub ) { return ( (Stub) widget ).getAttributes(); } return null; } @Override protected void layoutWidget( Widget widget, String elementName, Map attributes ) { GwtMetawidget.this.layoutWidget( widget, elementName, attributes ); super.layoutWidget( widget, elementName, attributes ); } @Override protected void endBuild() { GwtMetawidget.this.endBuild(); super.endBuild(); } @Override protected GwtMetawidget getPipelineOwner() { return GwtMetawidget.this; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy