View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    * 
10   *  http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   */
19  package org.apache.myfaces.trinidad.change;
20  
21  import java.util.HashMap;
22  
23  import javax.el.ValueExpression;
24  
25  import javax.faces.component.NamingContainer;
26  import javax.faces.component.UIComponent;
27  import javax.faces.context.FacesContext;
28  import javax.faces.el.ValueBinding;
29  
30  import org.apache.myfaces.trinidad.logging.TrinidadLogger;
31  
32  
33  /**
34   * The base class for all ChangeManagers.
35   * A ChangeManager should manage accumulation of Changes and also
36   *  take care of their persistence.
37   * @version $Name:  $ ($Revision: adfrt/faces/adf-faces-api/src/main/java/oracle/adf/view/faces/change/ChangeManager.java#0 $) $Date: 10-nov-2005.19:09:58 $
38   */
39  public abstract class ChangeManager
40  {
41    public static void registerDocumentFactory(
42      String targetClassName,
43      String converterClassName)
44    {
45      if ((targetClassName == null) || (targetClassName.length() == 0))
46        throw new IllegalArgumentException(_LOG.getMessage(
47          "TARGET_CLASS_NAME_MUST_BE_PROVIDED"));
48  
49      if ((converterClassName == null) || (converterClassName.length() == 0))
50        throw new IllegalArgumentException(_LOG.getMessage(
51          "CONVERTER_CLASS_NAME_MUST_BE_PROVIDED"));
52  
53      synchronized (_CLASSNAME_TO_CONVERTER_NAME_MAP)
54      {
55        _CLASSNAME_TO_CONVERTER_NAME_MAP.put(targetClassName, converterClassName);
56      }
57    }
58  
59    /**
60     * Use the conversion rules to attempt to retrieve the equivalent
61     * document change for a ComponentChange
62     * @param change to convert
63     */
64    protected static DocumentChange createDocumentChange(
65      ComponentChange change)
66    {
67      Class<? extends ComponentChange> changeClass = change.getClass();
68  
69      Object converterObject = null;
70      DocumentChangeFactory converter = null;
71  
72      synchronized (_CLASS_TO_CONVERTER_MAP)
73      {
74        converterObject = _CLASS_TO_CONVERTER_MAP.get(changeClass);
75      }
76  
77      if (converterObject != null)
78      {
79        converter = (DocumentChangeFactory)converterObject;
80      }
81      else
82      {
83        String converterName = null;
84  
85        synchronized (_CLASSNAME_TO_CONVERTER_NAME_MAP)
86        {
87         converterName = 
88                    _CLASSNAME_TO_CONVERTER_NAME_MAP.get(changeClass.getName());
89        }
90  
91        if (converterName != null)
92        {
93          try
94          {
95            ClassLoader contextClassLoader =
96              Thread.currentThread().getContextClassLoader();
97  
98            Class<?> converterClass = contextClassLoader.loadClass(converterName);
99            if (DocumentChangeFactory.class.isAssignableFrom(converterClass))
100           {
101             converter = (DocumentChangeFactory)converterClass.newInstance();
102 
103             synchronized (_CLASS_TO_CONVERTER_MAP)
104             {
105               _CLASS_TO_CONVERTER_MAP.put(changeClass, converter);
106             }
107           }
108           else
109           {
110             // log warning because class isn't correct type
111             _LOG.warning("CONVERSION_CLASS_TYPE", new Object[] {converterClass, DocumentChangeFactory.class}); // NOTRANS
112           }
113         }
114         catch (Throwable e)
115         {
116           _LOG.warning("UNABLE_INSTANTIATE_CONVERTERCLASS", converterName); // NOTRANS
117           _LOG.warning(e);
118         }
119 
120 	// if the registered converter class name doesn't work remove
121 	// it from _CLASSNAME_TO_CONVERT_NAME_MAP
122         if (converter == null)
123         {
124           // this entry doesn't work, so remove it
125           _CLASSNAME_TO_CONVERTER_NAME_MAP.remove(converterName);
126 
127           return null;
128         }
129       }
130     }
131 
132     // return the converted object
133     if (converter != null)
134       return converter.convert(change);
135     
136     return null;
137   }
138 
139   /**
140    * Add a ComponentChange to this current request for a specified component.
141    * @throws IllegalArgumentException if any of the supplied parameters were to
142    *          be null.
143    */
144   public abstract void addComponentChange(
145     FacesContext facesContext,
146     UIComponent uiComponent,
147     ComponentChange change);
148 
149   /**
150    * Add a DocumentChange to this current request for a specified component.
151    * @throws IllegalArgumentException if any of the supplied parameters were to
152    *          be null.
153    */
154   public void addDocumentChange(
155       FacesContext facesContext,
156       UIComponent uiComponent,
157       DocumentChange change)
158   {
159     if (facesContext == null || uiComponent == null || change == null)
160       throw new IllegalArgumentException(_LOG.getMessage(
161         "CANNOT_ADD_CHANGE_WITH_FACECONTEXT_OR_UICOMPONENT_OR_NULL"));
162   }
163 
164   /**
165    * Applies all the ComponentChanges added so far for the current view.
166    * Developers should not need to call this method. Internal implementation
167    * will call it as the component tree is built and is ready to take changes.
168    * @param facesContext The FacesContext instance for the current request.
169    */
170   public void applyComponentChangesForCurrentView(FacesContext facesContext)
171   {
172     throw new UnsupportedOperationException("Subclassers must implement");
173   }
174 
175   /**
176    * Applies the ComponentChanges added so far for components underneath
177    * the specified NamingContainer.
178    * Developers should not need to call this method. Internal implementation
179    * will call it as the component tree is built and is ready to take changes.
180    * @param facesContext The FacesContext instance for the current request.
181    * @param root The NamingContainer that contains the component subtree
182    * to which ComponentChanges should be applied.  If null, all changes are
183    * applied.
184    * @throws IllegalArgumentException if the root NamingContainer is not a
185    *   UIComponent instance.
186    */
187   public void applyComponentChangesForSubtree(
188     FacesContext facesContext,
189     NamingContainer root
190     )
191   {
192     throw new UnsupportedOperationException("Subclassers must implement");
193   }
194   
195   private static class AttributeConverter extends DocumentChangeFactory
196   {
197     @Override
198     public DocumentChange convert(ComponentChange compChange)
199     {
200       if (compChange instanceof AttributeComponentChange)
201       {
202         AttributeComponentChange change = (AttributeComponentChange)compChange;
203 
204         Object value = change.getAttributeValue();
205 
206         // =-= bts TODO add registration of attribute converters
207         String valueString = null;
208         if ((value == null) ||
209             (value instanceof CharSequence) ||
210             (value instanceof Number) ||
211             (value instanceof Boolean))
212         {
213           valueString = (value != null)? value.toString() : null;
214         }
215         else if (value instanceof ValueExpression)
216         {
217           valueString = ((ValueExpression)value).getExpressionString();
218         }
219         else if (value instanceof ValueBinding)
220         {
221           valueString = ((ValueBinding)value).getExpressionString();
222         }
223         
224         if (valueString != null)
225           return new AttributeDocumentChange(change.getAttributeName(),
226                                              valueString);
227       }
228 
229       // no conversion possible
230       return null;
231     }
232   }
233 
234   private static HashMap<String, String> _CLASSNAME_TO_CONVERTER_NAME_MAP =
235     new HashMap<String, String>();
236   
237   private static HashMap<Class<? extends ComponentChange>, DocumentChangeFactory> _CLASS_TO_CONVERTER_MAP = 
238     new HashMap<Class<? extends ComponentChange>, DocumentChangeFactory>();
239 
240   static private final TrinidadLogger _LOG = 
241      TrinidadLogger.createTrinidadLogger(ChangeManager.class);
242 
243   static
244   {
245     // register the attribute converter
246     _CLASS_TO_CONVERTER_MAP.put(AttributeComponentChange.class,
247                                 new AttributeConverter());
248   }
249 
250 }