Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.metawidget.vaadin.ui.VaadinMetawidget Maven / Gradle / Ivy
// 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.vaadin.ui;
import static org.metawidget.inspector.InspectionResultConstants.*;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import org.metawidget.iface.MetawidgetException;
import org.metawidget.inspectionresultprocessor.iface.InspectionResultProcessor;
import org.metawidget.inspector.iface.Inspector;
import org.metawidget.layout.iface.Layout;
import org.metawidget.pipeline.w3c.W3CPipeline;
import org.metawidget.util.ArrayUtils;
import org.metawidget.util.ClassUtils;
import org.metawidget.util.CollectionUtils;
import org.metawidget.util.simple.PathUtils;
import org.metawidget.util.simple.PathUtils.TypeAndNames;
import org.metawidget.util.simple.StringUtils;
import org.metawidget.widgetbuilder.iface.WidgetBuilder;
import org.metawidget.widgetprocessor.iface.WidgetProcessor;
import org.w3c.dom.Element;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.Component;
import com.vaadin.ui.ComponentContainer;
import com.vaadin.ui.CustomComponent;
/**
* Metawidget for Vaadin environments.
*
* @author Loghman Barari
*/
public class VaadinMetawidget
extends CustomComponent {
//
// Private members
//
private Object mToInspect;
private String mPath;
private ResourceBundle mBundle;
private boolean mNeedToBuildWidgets;
private Element mLastInspection;
private boolean mIgnoreAddRemove;
/**
* List of existing, manually added components.
*
* This is a List, not a Set, so that mExistingUnusedComponents (which is initialized from it)
* is consistent.
*/
private List mExistingComponents = CollectionUtils.newArrayList();
/**
* List of existing, manually added, but unused by Metawidget components.
*
* This is a List, not a Set, for consistency during endBuild.
*/
private List mExistingUnusedComponents;
private Map mFacets = CollectionUtils.newHashMap();
private Map mClientProperties;
/* package private */Pipeline mPipeline;
//
// Constructor
//
public VaadinMetawidget() {
mPipeline = newPipeline();
}
//
// Public methods
//
/**
* 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();
}
/**
* 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 path to be inspected.
*/
public void setPath( String path ) {
mPath = path;
invalidateInspection();
}
public String getPath() {
return mPath;
}
public void setConfig( String config ) {
mPipeline.setConfig( config );
invalidateInspection();
}
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 removeInspectionResultProcessor( InspectionResultProcessor inspectionResultProcessor ) {
mPipeline.removeInspectionResultProcessor( inspectionResultProcessor );
invalidateInspection();
}
public void setInspectionResultProcessors( InspectionResultProcessor... inspectionResultProcessors ) {
mPipeline.setInspectionResultProcessors( inspectionResultProcessors );
invalidateInspection();
}
public void setWidgetBuilder( WidgetBuilder widgetBuilder ) {
mPipeline.setWidgetBuilder( widgetBuilder );
invalidateWidgets();
}
public void addWidgetProcessor( WidgetProcessor widgetProcessor ) {
mPipeline.addWidgetProcessor( widgetProcessor );
invalidateWidgets();
}
public void removeWidgetProcessor( WidgetProcessor widgetProcessor ) {
mPipeline.removeWidgetProcessor( widgetProcessor );
invalidateWidgets();
}
public void setWidgetProcessors( WidgetProcessor... widgetProcessors ) {
mPipeline.setWidgetProcessors( widgetProcessors );
invalidateWidgets();
}
public T getWidgetProcessor( Class widgetProcessorClass ) {
buildWidgets();
return mPipeline.getWidgetProcessor( widgetProcessorClass );
}
/**
* Set the layout for this Metawidget.
*/
public void setLayout( Layout layout ) {
mPipeline.setLayout( layout );
invalidateWidgets();
}
public ResourceBundle getBundle() {
return mBundle;
}
public void setBundle( ResourceBundle bundle ) {
mBundle = bundle;
invalidateWidgets();
}
/**
* Returns a label for the given set of attributes.
*
* The label is determined using the following algorithm:
*
*
* if attributes.get( "label" ) exists...
*
* attributes.get( "label" ) is camel-cased and used as a lookup into
* getLocalizedKey( camelCasedLabel ) . This means developers can initially build their
* UIs without worrying about localization, then turn it on later
* if no such lookup exists, return attributes.get( "label" )
*
*
* if attributes.get( "label" ) does not exist...
*
* attributes.get( "name" ) is used as a lookup into
* getLocalizedKey( name )
* if no such lookup exists, return attributes.get( "name" )
*
*
*
*/
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 ( mBundle == null ) {
return null;
}
try {
return mBundle.getString( key );
} catch ( MissingResourceException e ) {
return StringUtils.RESOURCE_KEY_NOT_FOUND_PREFIX + key + StringUtils.RESOURCE_KEY_NOT_FOUND_SUFFIX;
}
}
@Override
public boolean isReadOnly() {
return mPipeline.isReadOnly();
}
@Override
public void setReadOnly( boolean readOnly ) {
if ( mPipeline.isReadOnly() == readOnly ) {
return;
}
mPipeline.setReadOnly( readOnly );
invalidateWidgets();
}
public int getMaximumInspectionDepth() {
return mPipeline.getMaximumInspectionDepth();
}
public void setMaximumInspectionDepth( int maximumInspectionDepth ) {
mPipeline.setMaximumInspectionDepth( maximumInspectionDepth );
invalidateWidgets();
}
/**
* Fetch a list of AbstractComponents
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 List fetchExistingUnusedComponents() {
return mExistingUnusedComponents;
}
//
// The following methods all kick off buildWidgets() if necessary
//
@Override
public int getComponentCount() {
buildWidgets();
return super.getComponentCount();
}
@Override
public Iterator getComponentIterator() {
buildWidgets();
return super.getComponentIterator();
}
/**
* Finds the Component with the given name.
*/
@SuppressWarnings( "unchecked" )
public T getComponent( String... names ) {
if ( names == null || names.length == 0 ) {
return null;
}
Component topComponent = this;
for ( int loop = 0, length = names.length; loop < length; loop++ ) {
String name = names[loop];
// May need building 'just in time' if we are calling getComponent
// immediately after a 'setToInspect'. See
// VaadinMetawidgetTest.testNestedWithManualInspector
if ( topComponent instanceof VaadinMetawidget ) {
( (VaadinMetawidget) topComponent ).buildWidgets();
}
// Try to find a component
if ( topComponent instanceof ComponentContainer ) {
topComponent = getComponent( (ComponentContainer) topComponent, name );
} else {
topComponent = null;
}
if ( loop == length - 1 ) {
return (T) topComponent;
}
if ( topComponent == null ) {
throw MetawidgetException.newException( "No such component '" + name + "' of '" + ArrayUtils.toString( names, "', '" ) + "'" );
}
}
return (T) topComponent;
}
public Facet getFacet( String name ) {
buildWidgets();
return mFacets.get( name );
}
/**
* Named after Panel.getContent
.
*/
@SuppressWarnings( "unchecked" )
public C getContent() {
buildWidgets();
return (C) getCompositionRoot();
}
@Override
public void addComponent( Component component ) {
if ( !mIgnoreAddRemove ) {
invalidateWidgets();
// Don't fall through to super.addImpl for facets. Tuck them away
// in mFacets instead. Some layouts may never use them, and
// others (eg. MigLayout) don't like adding components
// without constraints
AbstractComponent abstractComponent = (AbstractComponent) component;
if ( component instanceof Facet ) {
mFacets.put( abstractComponent.getData(), (Facet) component );
return;
}
mExistingComponents.add( abstractComponent );
} else {
setCompositionRoot( component );
}
}
@Override
public void removeComponent( Component component ) {
if ( !mIgnoreAddRemove ) {
invalidateWidgets();
if ( component instanceof Facet ) {
mFacets.remove( ( (Facet) component ).getData() );
} else {
mExistingComponents.remove( component );
}
}
}
@Override
public void paintContent( PaintTarget target )
throws PaintException {
buildWidgets();
super.paintContent( target );
}
/**
* 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 );
}
//
// 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 String getDefaultConfiguration() {
return ClassUtils.getPackagesAsFolderNames( VaadinMetawidget.class ) + "/metawidget-vaadin-default.xml";
}
/**
* 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.
*/
protected void invalidateWidgets() {
if ( mNeedToBuildWidgets ) {
return;
}
// Prepare to build widgets
this.mNeedToBuildWidgets = true;
if ( this.mClientProperties != null ) {
this.mClientProperties.clear();
}
// Call repaint here, rather than just 'invalidate', for scenarios like
// doing a 'remove' of a button that masks a Metawidget
this.requestRepaint();
}
protected void buildWidgets() {
// No need to build?
if ( !mNeedToBuildWidgets ) {
return;
}
mPipeline.configureOnce();
mNeedToBuildWidgets = false;
mIgnoreAddRemove = true;
try {
if ( mLastInspection == null ) {
mLastInspection = inspect();
}
if ( mPath != null ) {
mPipeline.buildWidgets( mLastInspection );
}
} catch ( Exception e ) {
throw MetawidgetException.newException( e );
} finally {
mIgnoreAddRemove = false;
}
}
protected void startBuild() {
mExistingUnusedComponents = CollectionUtils.newArrayList( mExistingComponents );
}
/**
* @param elementName
* XML node name of the business field. Typically 'entity',
* 'property' or 'action'. Never null
*/
protected void layoutWidget( Component component, String elementName, Map attributes ) {
// Set the name of the component.
//
// Note: we haven't split this out into a separate WidgetProcessor, because other methods
// like getValue/setValue/getComponent( String... names ) rely on it
( (AbstractComponent) component ).setData( attributes.get( NAME ) );
// Remove, then re-add to layout (to re-order the component)
removeComponent( component );
// Look up any additional attributes
Map additionalAttributes = mPipeline.getAdditionalAttributes( component );
if ( additionalAttributes != null ) {
attributes.putAll( additionalAttributes );
}
}
protected void endBuild() {
if ( mExistingUnusedComponents != null ) {
for ( Component componentExisting : mExistingUnusedComponents ) {
// Unused facets don't count
if ( componentExisting instanceof Facet ) {
continue;
}
// Manually created components default to no section
Map attributes = CollectionUtils.newHashMap();
attributes.put( SECTION, "" );
mPipeline.layoutWidget( componentExisting, PROPERTY, attributes );
}
}
}
protected void initNestedMetawidget( VaadinMetawidget nestedMetawidget, Map attributes ) {
// Don't copy setConfig(). Instead, copy runtime values
mPipeline.initNestedPipeline( nestedMetawidget.mPipeline, attributes );
nestedMetawidget.setPath( mPath + StringUtils.SEPARATOR_FORWARD_SLASH_CHAR + attributes.get( NAME ) );
nestedMetawidget.setBundle( mBundle );
nestedMetawidget.setToInspect( mToInspect );
}
//
// Private methods
//
/**
* Updates the Object to inspect, without invalidating the previous
* inspection results.
*/
private 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;
}
private Element inspect() {
if ( mPath == null ) {
return null;
}
TypeAndNames typeAndNames = PathUtils.parsePath( mPath );
return mPipeline.inspectAsDom( mToInspect, typeAndNames.getType(), typeAndNames.getNamesAsArray() );
}
private Component getComponent( ComponentContainer container, String name ) {
Iterator iterator = container.getComponentIterator();
while ( iterator.hasNext() ) {
AbstractComponent childComponent = (AbstractComponent) iterator.next();
// Drill into unnamed containers
if ( childComponent.getData() == null && childComponent instanceof ComponentContainer ) {
childComponent = (AbstractComponent) getComponent( (ComponentContainer) childComponent, name );
if ( childComponent != null ) {
return childComponent;
}
continue;
}
// Match by name
if ( name.equals( childComponent.getData() ) ) {
return childComponent;
}
}
// Not found
return null;
}
//
// Inner class
//
protected class Pipeline
extends W3CPipeline
implements Serializable {
//
// Protected methods
//
@Override
protected VaadinMetawidget getPipelineOwner() {
return VaadinMetawidget.this;
}
@Override
protected String getDefaultConfiguration() {
return VaadinMetawidget.this.getDefaultConfiguration();
}
@Override
protected void startBuild() {
VaadinMetawidget.this.startBuild();
super.startBuild();
}
@Override
protected void layoutWidget( Component component, String elementName, Map attributes ) {
VaadinMetawidget.this.layoutWidget( component, elementName, attributes );
super.layoutWidget( component, elementName, attributes );
}
@Override
protected Map getAdditionalAttributes( Component component ) {
if ( component instanceof Stub ) {
return ( (Stub) component ).getAttributes();
}
return null;
}
@Override
public VaadinMetawidget buildNestedMetawidget( Map attributes )
throws Exception {
if ( TRUE.equals( attributes.get( HIDDEN ) ) ) {
return null;
}
VaadinMetawidget nestedMetawidget = VaadinMetawidget.this.getClass().newInstance();
VaadinMetawidget.this.initNestedMetawidget( nestedMetawidget, attributes );
return nestedMetawidget;
}
@Override
protected void endBuild() {
VaadinMetawidget.this.endBuild();
super.endBuild();
}
}
}