Save This Page
Home » struts-2.1.8.1-src » org.apache » struts2 » components » [javadoc | source]
    1   /*
    2    * $Id: Component.java 768855 2009-04-27 02:09:35Z wesw $
    3    *
    4    * Licensed to the Apache Software Foundation (ASF) under one
    5    * or more contributor license agreements.  See the NOTICE file
    6    * distributed with this work for additional information
    7    * regarding copyright ownership.  The ASF licenses this file
    8    * to you under the Apache License, Version 2.0 (the
    9    * "License"); you may not use this file except in compliance
   10    * with the License.  You may obtain a copy of the License at
   11    *
   12    *  http://www.apache.org/licenses/LICENSE-2.0
   13    *
   14    * Unless required by applicable law or agreed to in writing,
   15    * software distributed under the License is distributed on an
   16    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   17    * KIND, either express or implied.  See the License for the
   18    * specific language governing permissions and limitations
   19    * under the License.
   20    */
   21   
   22   package org.apache.struts2.components;
   23   
   24   import java.io.IOException;
   25   import java.io.PrintWriter;
   26   import java.io.Writer;
   27   import java.util.Iterator;
   28   import java.util.LinkedHashMap;
   29   import java.util.Map;
   30   import java.util.Stack;
   31   
   32   import javax.servlet.http.HttpServletRequest;
   33   import javax.servlet.http.HttpServletResponse;
   34   
   35   import org.apache.struts2.StrutsException;
   36   import org.apache.struts2.StrutsConstants;
   37   import org.apache.struts2.dispatcher.mapper.ActionMapper;
   38   import org.apache.struts2.dispatcher.mapper.ActionMapping;
   39   import org.apache.struts2.util.FastByteArrayOutputStream;
   40   import org.apache.struts2.views.jsp.TagUtils;
   41   import org.apache.struts2.views.util.ContextUtil;
   42   import org.apache.struts2.views.util.UrlHelper;
   43   
   44   import com.opensymphony.xwork2.inject.Inject;
   45   import com.opensymphony.xwork2.util.ValueStack;
   46   import com.opensymphony.xwork2.util.TextParseUtil;
   47   
   48   /**
   49    * Base class to extend for UI components.
   50    * <p/>
   51    * This class is a good extension point when building reuseable UI components.
   52    *
   53    */
   54   public class Component {
   55       public static final String COMPONENT_STACK = "__component_stack";
   56   
   57       protected ValueStack stack;
   58       protected Map parameters;
   59       protected ActionMapper actionMapper;
   60       protected boolean throwExceptionOnELFailure;
   61   
   62       /**
   63        * Constructor.
   64        *
   65        * @param stack  OGNL value stack.
   66        */
   67       public Component(ValueStack stack) {
   68           this.stack = stack;
   69           this.parameters = new LinkedHashMap();
   70           getComponentStack().push(this);
   71       }
   72   
   73       /**
   74        * Gets the name of this component.
   75        * @return the name of this component.
   76        */
   77       private String getComponentName() {
   78           Class c = getClass();
   79           String name = c.getName();
   80           int dot = name.lastIndexOf('.');
   81   
   82           return name.substring(dot + 1).toLowerCase();
   83       }
   84       
   85       @Inject
   86       public void setActionMapper(ActionMapper mapper) {
   87           this.actionMapper = mapper;
   88       }
   89   
   90       @Inject(StrutsConstants.STRUTS_EL_THROW_EXCEPTION)
   91       public void setThrowExceptionsOnELFailure(String throwException) {
   92           this.throwExceptionOnELFailure = "true".equals(throwException);
   93       }
   94       
   95       /**
   96        * Gets the OGNL value stack assoicated with this component.
   97        * @return the OGNL value stack assoicated with this component.
   98        */
   99       public ValueStack getStack() {
  100           return stack;
  101       }
  102   
  103       /**
  104        * Gets the component stack of this component.
  105        * @return the component stack of this component, never <tt>null</tt>.
  106        */
  107       public Stack getComponentStack() {
  108           Stack componentStack = (Stack) stack.getContext().get(COMPONENT_STACK);
  109           if (componentStack == null) {
  110               componentStack = new Stack();
  111               stack.getContext().put(COMPONENT_STACK, componentStack);
  112           }
  113           return componentStack;
  114       }
  115   
  116       /**
  117        * Callback for the start tag of this component.
  118        * Should the body be evaluated?
  119        *
  120        * @param writer  the output writer.
  121        * @return true if the body should be evaluated
  122        */
  123       public boolean start(Writer writer) {
  124           return true;
  125       }
  126   
  127       /**
  128        * Callback for the end tag of this component.
  129        * Should the body be evaluated again?
  130        * <p/>
  131        * <b>NOTE:</b> will pop component stack.
  132        * @param writer  the output writer.
  133        * @param body    the rendered body.
  134        * @return true if the body should be evaluated again
  135        */
  136       public boolean end(Writer writer, String body) {
  137           return end(writer, body, true);
  138       }
  139   
  140       /**
  141        * Callback for the start tag of this component.
  142        * Should the body be evaluated again?
  143        * <p/>
  144        * <b>NOTE:</b> has a parameter to determine to pop the component stack.
  145        * @param writer  the output writer.
  146        * @param body    the rendered body.
  147        * @param popComponentStack  should the component stack be popped?
  148        * @return true if the body should be evaluated again
  149        */
  150       protected boolean end(Writer writer, String body, boolean popComponentStack) {
  151           assert(body != null);
  152   
  153           try {
  154               writer.write(body);
  155           } catch (IOException e) {
  156               throw new StrutsException("IOError while writing the body: " + e.getMessage(), e);
  157           }
  158           if (popComponentStack) {
  159               popComponentStack();
  160           }
  161           return false;
  162       }
  163   
  164       /**
  165        * Pops the component stack.
  166        */
  167       protected void popComponentStack() {
  168           getComponentStack().pop();
  169       }
  170   
  171       /**
  172        * Finds the nearest ancestor of this component stack.
  173        * @param clazz the class to look for, or if assignable from.
  174        * @return  the component if found, <tt>null</tt> if not.
  175        */
  176       protected Component findAncestor(Class clazz) {
  177           Stack componentStack = getComponentStack();
  178           int currPosition = componentStack.search(this);
  179           if (currPosition >= 0) {
  180               int start = componentStack.size() - currPosition - 1;
  181   
  182               //for (int i = componentStack.size() - 2; i >= 0; i--) {
  183               for (int i = start; i >=0; i--) {
  184                   Component component = (Component) componentStack.get(i);
  185                   if (clazz.isAssignableFrom(component.getClass()) && component != this) {
  186                       return component;
  187                   }
  188               }
  189           }
  190   
  191           return null;
  192       }
  193   
  194       /**
  195        * Evaluates the OGNL stack to find a String value.
  196        * @param expr  OGNL expression.
  197        * @return  the String value found.
  198        */
  199       protected String findString(String expr) {
  200           return (String) findValue(expr, String.class);
  201       }
  202   
  203       /**
  204        * Evaluates the OGNL stack to find a String value.
  205        * <p/>
  206        * If the given expression is <tt>null</tt/> a error is logged and a <code>RuntimeException</code> is thrown
  207        * constructed with a messaged based on the given field and errorMsg paramter.
  208        *
  209        * @param expr  OGNL expression.
  210        * @param field   field name used when throwing <code>RuntimeException</code>.
  211        * @param errorMsg  error message used when throwing <code>RuntimeException</code>.
  212        * @return  the String value found.
  213        * @throws StrutsException is thrown in case of expression is <tt>null</tt>.
  214        */
  215       protected String findString(String expr, String field, String errorMsg) {
  216           if (expr == null) {
  217               throw fieldError(field, errorMsg, null);
  218           } else {
  219               return findString(expr);
  220           }
  221       }
  222   
  223       /**
  224        * Constructs a <code>RuntimeException</code> based on the given information.
  225        * <p/>
  226        * A message is constructed and logged at ERROR level before being returned
  227        * as a <code>RuntimeException</code>.
  228        * @param field   field name used when throwing <code>RuntimeException</code>.
  229        * @param errorMsg  error message used when throwing <code>RuntimeException</code>.
  230        * @param e  the caused exception, can be <tt>null</tt>.
  231        * @return  the constructed <code>StrutsException</code>.
  232        */
  233       protected StrutsException fieldError(String field, String errorMsg, Exception e) {
  234           String msg = "tag '" + getComponentName() + "', field '" + field +
  235                   ( parameters != null && parameters.containsKey("name")?"', name '" + parameters.get("name"):"") +
  236                   "': " + errorMsg;
  237           throw new StrutsException(msg, e);
  238       }
  239   
  240       /**
  241        * Finds a value from the OGNL stack based on the given expression.
  242        * Will always evaluate <code>expr</code> against stack except when <code>expr</code>
  243        * is null. If altsyntax (%{...}) is applied, simply strip it off.
  244        *
  245        * @param expr  the expression. Returns <tt>null</tt> if expr is null.
  246        * @return the value, <tt>null</tt> if not found.
  247        */
  248       protected Object findValue(String expr) {
  249           if (expr == null) {
  250               return null;
  251           }
  252   
  253           expr = stripExpressionIfAltSyntax(expr);
  254   
  255           return getStack().findValue(expr, throwExceptionOnELFailure);
  256       }
  257   
  258       /**
  259        * If altsyntax (%{...}) is applied, simply strip the "%{" and "}" off. 
  260        * @param expr the expression (must be not null)
  261        * @return the stripped expression if altSyntax is enabled. Otherwise
  262        * the parameter expression is returned as is.
  263        */
  264   	protected String stripExpressionIfAltSyntax(String expr) {
  265   		return stripExpressionIfAltSyntax(stack, expr);
  266   	}
  267   	
  268       /**
  269        * If altsyntax (%{...}) is applied, simply strip the "%{" and "}" off.
  270        * @param stack the ValueStack where the context value is searched for. 
  271        * @param expr the expression (must be not null)
  272        * @return the stripped expression if altSyntax is enabled. Otherwise
  273        * the parameter expression is returned as is.
  274        */
  275   	public static String stripExpressionIfAltSyntax(ValueStack stack, String expr) {
  276   		if (altSyntax(stack)) {
  277               // does the expression start with %{ and end with }? if so, just cut it off!
  278               if (expr.startsWith("%{") && expr.endsWith("}")) {
  279                   return expr.substring(2, expr.length() - 1);
  280               }
  281           }
  282   		return expr;
  283   	}
  284   
  285       /**
  286        * Is the altSyntax enabled? [TRUE]
  287        * <p/>
  288        * @param stack the ValueStack where the context value is searched for.
  289        * @return true if altSyntax is activated. False otherwise. 
  290        * See <code>struts.properties</code> where the altSyntax flag is defined.
  291        */
  292   	public static boolean altSyntax(ValueStack stack)  {
  293           return ContextUtil.isUseAltSyntax(stack.getContext());
  294   	}
  295   
  296       /**
  297        * Is the altSyntax enabled? [TRUE]
  298        * <p/>
  299        * See <code>struts.properties</code> where the altSyntax flag is defined.
  300        */
  301       public boolean altSyntax() {
  302           return altSyntax(stack);
  303       }
  304   
  305       /**
  306        * Adds the sorrounding %{ } to the expression for proper processing.
  307        * @param expr the expression.
  308        * @return the modified expression if altSyntax is enabled, or the parameter 
  309        * expression otherwise.
  310        */
  311   	protected String completeExpressionIfAltSyntax(String expr) {
  312   		if (altSyntax()) {
  313   			return "%{" + expr + "}";
  314   		}
  315   		return expr;
  316   	}
  317   
  318       /**
  319        * This check is needed for backwards compatibility with 2.1.x
  320        * @param expr the expression.
  321        * @return the found string if altSyntax is enabled. The parameter
  322        * expression otherwise.
  323        */
  324   	protected String findStringIfAltSyntax(String expr) {
  325   		if (altSyntax()) {
  326   		    return findString(expr);
  327   		}
  328   		return expr;
  329   	}
  330   
  331       /**
  332        * Evaluates the OGNL stack to find an Object value.
  333        * <p/>
  334        * Function just like <code>findValue(String)</code> except that if the
  335        * given expression is <tt>null</tt/> a error is logged and
  336        * a <code>RuntimeException</code> is thrown constructed with a
  337        * messaged based on the given field and errorMsg paramter.
  338        *
  339        * @param expr  OGNL expression.
  340        * @param field   field name used when throwing <code>RuntimeException</code>.
  341        * @param errorMsg  error message used when throwing <code>RuntimeException</code>.
  342        * @return  the Object found, is never <tt>null</tt>.
  343        * @throws StrutsException is thrown in case of not found in the OGNL stack, or expression is <tt>null</tt>.
  344        */
  345       protected Object findValue(String expr, String field, String errorMsg) {
  346           if (expr == null) {
  347               throw fieldError(field, errorMsg, null);
  348           } else {
  349               Object value = null;
  350               Exception problem = null;
  351               try {
  352                   value = findValue(expr);
  353               } catch (Exception e) {
  354                   problem = e;
  355               }
  356   
  357               if (value == null) {
  358                   throw fieldError(field, errorMsg, problem);
  359               }
  360   
  361               return value;
  362           }
  363       }
  364   
  365       /**
  366        * Evaluates the OGNL stack to find an Object of the given type. Will evaluate
  367        * <code>expr</code> the portion wrapped with altSyntax (%{...})
  368        * against stack when altSyntax is on, else the whole <code>expr</code>
  369        * is evaluated against the stack.
  370        * <p/>
  371        * This method only supports the altSyntax. So this should be set to true.
  372        * @param expr  OGNL expression.
  373        * @param toType  the type expected to find.
  374        * @return  the Object found, or <tt>null</tt> if not found.
  375        */
  376       protected Object findValue(String expr, Class toType) {
  377           if (altSyntax() && toType == String.class) {
  378           	return TextParseUtil.translateVariables('%', expr, stack);
  379           } else {
  380               expr = stripExpressionIfAltSyntax(expr);
  381   
  382               return getStack().findValue(expr, toType, throwExceptionOnELFailure);
  383           }
  384       }
  385   
  386       /**
  387        * Renders an action URL by consulting the {@link org.apache.struts2.dispatcher.mapper.ActionMapper}.
  388        * @param action      the action
  389        * @param namespace   the namespace
  390        * @param method      the method
  391        * @param req         HTTP request
  392        * @param res         HTTP response
  393        * @param parameters  parameters
  394        * @param scheme      http or https
  395        * @param includeContext  should the context path be included or not
  396        * @param encodeResult    should the url be encoded
  397        * @param forceAddSchemeHostAndPort    should the scheme host and port be forced
  398        * @param escapeAmp    should ampersand (&) be escaped to &amp;
  399        * @return the action url.
  400        */
  401       protected String determineActionURL(String action, String namespace, String method,
  402                                           HttpServletRequest req, HttpServletResponse res, Map parameters, String scheme,
  403                                           boolean includeContext, boolean encodeResult, boolean forceAddSchemeHostAndPort,
  404                                           boolean escapeAmp) {
  405           String finalAction = findString(action);
  406           String finalMethod = method != null ? findString(method) : null;
  407           String finalNamespace = determineNamespace(namespace, getStack(), req);
  408           ActionMapping mapping = new ActionMapping(finalAction, finalNamespace, finalMethod, parameters);
  409           String uri = actionMapper.getUriFromActionMapping(mapping);
  410           return UrlHelper.buildUrl(uri, req, res, parameters, scheme, includeContext, encodeResult, forceAddSchemeHostAndPort, escapeAmp);
  411       }
  412   
  413       /**
  414        * Determines the namespace of the current page being renderdd. Useful for Form, URL, and href generations.
  415        * @param namespace  the namespace
  416        * @param stack      OGNL value stack
  417        * @param req        HTTP request
  418        * @return  the namepsace of the current page being rendered, is never <tt>null</tt>.
  419        */
  420       protected String determineNamespace(String namespace, ValueStack stack, HttpServletRequest req) {
  421           String result;
  422   
  423           if (namespace == null) {
  424               result = TagUtils.buildNamespace(actionMapper, stack, req);
  425           } else {
  426               result = findString(namespace);
  427           }
  428   
  429           if (result == null) {
  430               result = "";
  431           }
  432   
  433           return result;
  434       }
  435   
  436       /**
  437        * Pushes this component's parameter Map as well as the component itself on to the stack
  438        * and then copies the supplied parameters over. Because the component's parameter Map is
  439        * pushed before the component itself, any key-value pair that can't be assigned to componet
  440        * will be set in the parameters Map.
  441        *
  442        * @param params  the parameters to copy.
  443        */
  444       public void copyParams(Map params) {
  445           stack.push(parameters);
  446           stack.push(this);
  447           try {
  448               for (Iterator iterator = params.entrySet().iterator(); iterator.hasNext();) {
  449                   Map.Entry entry = (Map.Entry) iterator.next();
  450                   String key = (String) entry.getKey();
  451                   stack.setValue(key, entry.getValue());
  452               }
  453           } finally {
  454               stack.pop();
  455               stack.pop();
  456           }
  457       }
  458   
  459       /**
  460        * Constructs a string representation of the given exception.
  461        * @param t  the exception
  462        * @return the exception as a string.
  463        */
  464       protected String toString(Throwable t) {
  465           FastByteArrayOutputStream bout = new FastByteArrayOutputStream();
  466           PrintWriter wrt = new PrintWriter(bout);
  467           t.printStackTrace(wrt);
  468           wrt.close();
  469   
  470           return bout.toString();
  471       }
  472   
  473       /**
  474        * Gets the parameters.
  475        * @return the parameters. Is never <tt>null</tt>.
  476        */
  477       public Map getParameters() {
  478           return parameters;
  479       }
  480   
  481       /**
  482        * Adds all the given parameters to this component's own parameters.
  483        * @param params the parameters to add.
  484        */
  485       public void addAllParameters(Map params) {
  486           parameters.putAll(params);
  487       }
  488   
  489       /**
  490        * Adds the given key and value to this component's own parameter.
  491        * <p/>
  492        * If the provided key is <tt>null</tt> nothing happens.
  493        * If the provided value is <tt>null</tt> any existing parameter with
  494        * the given key name is removed.
  495        * @param key  the key of the new parameter to add.
  496        * @param value the value assoicated with the key.
  497        */
  498       public void addParameter(String key, Object value) {
  499           if (key != null) {
  500               Map params = getParameters();
  501   
  502               if (value == null) {
  503                   params.remove(key);
  504               } else {
  505                   params.put(key, value);
  506               }
  507           }
  508       }
  509   
  510       /**
  511        * Overwrite to set if body shold be used.
  512        * @return always false for this component.
  513        */
  514       public boolean usesBody() {
  515           return false;
  516       }
  517   }

Save This Page
Home » struts-2.1.8.1-src » org.apache » struts2 » components » [javadoc | source]