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

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