001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *   http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    
020    package org.apache.myfaces.tobago.taglib.component;
021    
022    import org.apache.myfaces.tobago.apt.annotation.BodyContent;
023    import org.apache.myfaces.tobago.apt.annotation.Tag;
024    import org.apache.myfaces.tobago.apt.annotation.TagAttribute;
025    import org.apache.myfaces.tobago.component.ComponentUtil;
026    import org.apache.myfaces.tobago.event.TabChangeListener;
027    import org.apache.myfaces.tobago.event.TabChangeSource;
028    import org.apache.myfaces.tobago.event.TabChangeListenerValueBindingDelegate;
029    import org.apache.myfaces.tobago.event.TabChangeEvent;
030    
031    import org.apache.commons.logging.Log;
032    import org.apache.commons.logging.LogFactory;
033    
034    import javax.faces.component.UIComponent;
035    import javax.faces.context.FacesContext;
036    import javax.faces.el.ValueBinding;
037    import javax.faces.el.MethodBinding;
038    import javax.faces.webapp.UIComponentTag;
039    import javax.faces.application.Application;
040    import javax.servlet.jsp.JspException;
041    import javax.servlet.jsp.tagext.TagSupport;
042    
043    /**
044     * Register an TabChangedListener instance on the UIComponent
045     * associated with the closest parent UIComponent custom action.
046     */
047    @Tag(name = "tabChangeListener", bodyContent = BodyContent.EMPTY)
048    public class TabChangeListenerTag extends TagSupport {
049    
050      private static final long serialVersionUID = -419199086962377873L;
051    
052      private static final Class[] TAB_CHANGE_LISTENER_ARGS = new Class[] {TabChangeEvent.class};
053    
054      private static final Log LOG = LogFactory.getLog(TabChangeListenerTag.class);
055    
056      /**
057       * <p>The fully qualified class name of the {@link TabChangeListener}
058       * instance to be created.</p>
059       */
060      private String type;
061      private String binding;
062      private String listener;
063    
064      /**
065       * Fully qualified Java class name of a TabChangeListener to be
066       * created and registered.
067       */
068      @TagAttribute(required = true)
069      public void setType(String type) {
070        this.type = type;
071      }
072    
073      /**
074       * The value binding expression to a TabChangeListener.
075       */
076      @TagAttribute
077      public void setBinding(String binding) {
078        this.binding = binding;
079      }
080    
081    
082      /**
083       * A method binding expression to a processTabChange(TabChangeEvent tabChangeEvent) method.
084       */
085      @TagAttribute
086      public void setListener(String listener) {
087        this.listener = listener;
088      }
089    
090        /**
091       * <p>Create a new instance of the specified {@link TabChangeListener}
092       * class, and register it with the {@link javax.faces.component.UIComponent} instance associated
093       * with our most immediately surrounding {@link javax.faces.webapp.UIComponentTag} instance, if
094       * the {@link javax.faces.component.UIComponent} instance was created by this execution of the
095       * containing JSP page.</p>
096       *
097       * @throws JspException if a JSP error occurs
098       */
099      public int doStartTag() throws JspException {
100    
101        // Locate our parent UIComponentTag
102        UIComponentTag tag =
103            UIComponentTag.getParentUIComponentTag(pageContext);
104        if (tag == null) {
105          // TODO Message resource i18n
106          throw new JspException("Not nested in faces tag");
107        }
108    
109        if (!tag.getCreated()) {
110          return (SKIP_BODY);
111        }
112    
113        UIComponent component = tag.getComponentInstance();
114        if (component == null) {
115          // TODO Message resource i18n
116          throw new JspException("Component Instance is null");
117        }
118        if (!(component instanceof TabChangeSource)) {
119          // TODO Message resource i18n
120          throw new JspException("Component " + component.getClass().getName() + " is not instanceof TabChangeSource");
121        }
122        TabChangeSource changeSource = (TabChangeSource) component;
123    
124        TabChangeListener handler = null;
125        ValueBinding valueBinding = null;
126        if (binding != null && UIComponentTag.isValueReference(binding)) {
127          valueBinding = ComponentUtil.createValueBinding(binding);
128          if (valueBinding != null) {
129            Object obj = valueBinding.getValue(FacesContext.getCurrentInstance());
130            if (obj != null && obj instanceof TabChangeListener) {
131              handler = (TabChangeListener) obj;
132            }
133          }
134        }
135    
136        if (handler == null && type != null) {
137          handler = createTabChangeListener(type);
138          if (handler != null && valueBinding != null) {
139            valueBinding.setValue(FacesContext.getCurrentInstance(), handler);
140          }
141        }
142        if (handler != null) {
143          if (valueBinding != null) {
144            changeSource.addTabChangeListener(new TabChangeListenerValueBindingDelegate(type, valueBinding));
145          } else {
146            changeSource.addTabChangeListener(handler);
147          }
148        }
149    
150        if (listener != null && UIComponentTag.isValueReference(listener)) {
151          Application application = FacesContext.getCurrentInstance().getApplication();
152          MethodBinding methodBinding = application.createMethodBinding(listener, TAB_CHANGE_LISTENER_ARGS);
153          changeSource.setTabChangeListener(methodBinding);
154        }
155        // TODO else LOG.warn?
156        return (SKIP_BODY);
157      }
158    
159    
160      /**
161       * <p>Release references to any acquired resources.
162       */
163      public void release() {
164        this.type = null;
165        this.binding = null;
166      }
167    
168      /**
169       * <p>Create and return a new {@link TabChangeListener} to be registered
170       * on our surrounding {@link javax.faces.component.UIComponent}.</p>
171       *
172       * @throws javax.servlet.jsp.JspException if a new instance cannot be created
173       */
174      protected TabChangeListener createTabChangeListener(String className) throws JspException {
175        try {
176          Class clazz = getClass().getClassLoader().loadClass(className);
177          return ((TabChangeListener) clazz.newInstance());
178        } catch (Exception e) {
179          throw new JspException(e);
180        }
181      }
182    }