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

org.metawidget.vaadin.ui.VaadinMetawidget 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.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(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy