001: /*
002: * Copyright 2002-2005 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.web.servlet.tags;
018:
019: import javax.servlet.jsp.JspException;
020: import javax.servlet.jsp.PageContext;
021: import javax.servlet.jsp.tagext.TagSupport;
022:
023: import org.springframework.validation.Errors;
024: import org.springframework.web.util.ExpressionEvaluationUtils;
025:
026: /**
027: * <p>Nested-path tag, to support and assist with nested beans or bean properties
028: * in the model. Exports a "nestedPath" variable of type String in request scope,
029: * visible to the current page and also included pages, if any.
030: *
031: * <p>The BindTag will auto-detect the current nested path and automatically
032: * prepend it to its own path to form a complete path to the bean or bean property.
033: *
034: * <p>This tag will also prepend any existing nested path that is currently set.
035: * Thus, you can nest multiple nested-path tags.
036: *
037: * <p>Thanks to Seth Ladd for the suggestion and the original implementation!
038: *
039: * @author Juergen Hoeller
040: * @since 1.1
041: */
042: public class NestedPathTag extends TagSupport {
043:
044: /**
045: * Name of the exposed variable within the scope of this tag: "nestedPath".
046: */
047: public static final String NESTED_PATH_VARIABLE_NAME = "nestedPath";
048:
049: private String path;
050:
051: /** To cache any previous nested path, so that it may be reset */
052: private String previousNestedPath = "";
053:
054: /**
055: * Set the path that this tag should apply.
056: * <p>E.g. "customer" to allow bind paths like "address.street"
057: * rather than "customer.address.street".
058: * @see BindTag#setPath
059: */
060: public void setPath(String path) {
061: if (path == null) {
062: path = "";
063: }
064: if (path.length() > 0
065: && !path.endsWith(Errors.NESTED_PATH_SEPARATOR)) {
066: path += Errors.NESTED_PATH_SEPARATOR;
067: }
068: this .path = path;
069: }
070:
071: /**
072: * Return the path that this tag applies to.
073: */
074: public String getPath() {
075: return path;
076: }
077:
078: public int doStartTag() throws JspException {
079: String resolvedPath = ExpressionEvaluationUtils.evaluateString(
080: "path", getPath(), pageContext);
081:
082: // Save previous nestedPath value, build and expose current nestedPath value.
083: // Use request scope to expose nestedPath to included pages too.
084: String nestedPath = (String) pageContext.getAttribute(
085: NESTED_PATH_VARIABLE_NAME, PageContext.REQUEST_SCOPE);
086: if (nestedPath != null) {
087: this .previousNestedPath = nestedPath;
088: nestedPath = nestedPath + resolvedPath;
089: } else {
090: nestedPath = resolvedPath;
091: }
092: pageContext.setAttribute(NESTED_PATH_VARIABLE_NAME, nestedPath,
093: PageContext.REQUEST_SCOPE);
094:
095: return EVAL_BODY_INCLUDE;
096: }
097:
098: /**
099: * Reset any previous nestedPath value.
100: */
101: public int doEndTag() {
102: if (this .previousNestedPath != null) {
103: // Expose previous nestedPath value.
104: pageContext.setAttribute(NESTED_PATH_VARIABLE_NAME,
105: this .previousNestedPath, PageContext.REQUEST_SCOPE);
106: } else {
107: // Remove exposed nestedPath value.
108: pageContext.removeAttribute(NESTED_PATH_VARIABLE_NAME,
109: PageContext.REQUEST_SCOPE);
110: }
111:
112: return EVAL_PAGE;
113: }
114:
115: }
|