001/*
002 * Copyright 2002-2018 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 *      https://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
017package org.springframework.web.util;
018
019import javax.servlet.jsp.PageContext;
020import javax.servlet.jsp.tagext.Tag;
021
022import org.springframework.util.Assert;
023
024/**
025 * Utility class for tag library related code, exposing functionality
026 * such as translating {@link String Strings} to web scopes.
027 *
028 * <p>
029 * <ul>
030 * <li>{@code page} will be transformed to
031 * {@link javax.servlet.jsp.PageContext#PAGE_SCOPE PageContext.PAGE_SCOPE}
032 * <li>{@code request} will be transformed to
033 * {@link javax.servlet.jsp.PageContext#REQUEST_SCOPE PageContext.REQUEST_SCOPE}
034 * <li>{@code session} will be transformed to
035 * {@link javax.servlet.jsp.PageContext#SESSION_SCOPE PageContext.SESSION_SCOPE}
036 * <li>{@code application} will be transformed to
037 * {@link javax.servlet.jsp.PageContext#APPLICATION_SCOPE PageContext.APPLICATION_SCOPE}
038 * </ul>
039 *
040 * @author Alef Arendsen
041 * @author Rob Harrop
042 * @author Juergen Hoeller
043 * @author Rick Evans
044 */
045public abstract class TagUtils {
046
047        /** Constant identifying the page scope. */
048        public static final String SCOPE_PAGE = "page";
049
050        /** Constant identifying the request scope. */
051        public static final String SCOPE_REQUEST = "request";
052
053        /** Constant identifying the session scope. */
054        public static final String SCOPE_SESSION = "session";
055
056        /** Constant identifying the application scope. */
057        public static final String SCOPE_APPLICATION = "application";
058
059
060        /**
061         * Determines the scope for a given input {@code String}.
062         * <p>If the {@code String} does not match 'request', 'session',
063         * 'page' or 'application', the method will return {@link PageContext#PAGE_SCOPE}.
064         * @param scope the {@code String} to inspect
065         * @return the scope found, or {@link PageContext#PAGE_SCOPE} if no scope matched
066         * @throws IllegalArgumentException if the supplied {@code scope} is {@code null}
067         */
068        public static int getScope(String scope) {
069                Assert.notNull(scope, "Scope to search for cannot be null");
070                if (scope.equals(SCOPE_REQUEST)) {
071                        return PageContext.REQUEST_SCOPE;
072                }
073                else if (scope.equals(SCOPE_SESSION)) {
074                        return PageContext.SESSION_SCOPE;
075                }
076                else if (scope.equals(SCOPE_APPLICATION)) {
077                        return PageContext.APPLICATION_SCOPE;
078                }
079                else {
080                        return PageContext.PAGE_SCOPE;
081                }
082        }
083
084        /**
085         * Determine whether the supplied {@link Tag} has any ancestor tag
086         * of the supplied type.
087         * @param tag the tag whose ancestors are to be checked
088         * @param ancestorTagClass the ancestor {@link Class} being searched for
089         * @return {@code true} if the supplied {@link Tag} has any ancestor tag
090         * of the supplied type
091         * @throws IllegalArgumentException if either of the supplied arguments is {@code null};
092         * or if the supplied {@code ancestorTagClass} is not type-assignable to
093         * the {@link Tag} class
094         */
095        public static boolean hasAncestorOfType(Tag tag, Class<?> ancestorTagClass) {
096                Assert.notNull(tag, "Tag cannot be null");
097                Assert.notNull(ancestorTagClass, "Ancestor tag class cannot be null");
098                if (!Tag.class.isAssignableFrom(ancestorTagClass)) {
099                        throw new IllegalArgumentException(
100                                        "Class '" + ancestorTagClass.getName() + "' is not a valid Tag type");
101                }
102                Tag ancestor = tag.getParent();
103                while (ancestor != null) {
104                        if (ancestorTagClass.isAssignableFrom(ancestor.getClass())) {
105                                return true;
106                        }
107                        ancestor = ancestor.getParent();
108                }
109                return false;
110        }
111
112        /**
113         * Determine whether the supplied {@link Tag} has any ancestor tag
114         * of the supplied type, throwing an {@link IllegalStateException}
115         * if not.
116         * @param tag the tag whose ancestors are to be checked
117         * @param ancestorTagClass the ancestor {@link Class} being searched for
118         * @param tagName the name of the {@code tag}; for example '{@code option}'
119         * @param ancestorTagName the name of the ancestor {@code tag}; for example '{@code select}'
120         * @throws IllegalStateException if the supplied {@code tag} does not
121         * have a tag of the supplied {@code parentTagClass} as an ancestor
122         * @throws IllegalArgumentException if any of the supplied arguments is {@code null},
123         * or in the case of the {@link String}-typed arguments, is composed wholly
124         * of whitespace; or if the supplied {@code ancestorTagClass} is not
125         * type-assignable to the {@link Tag} class
126         * @see #hasAncestorOfType(javax.servlet.jsp.tagext.Tag, Class)
127         */
128        public static void assertHasAncestorOfType(Tag tag, Class<?> ancestorTagClass, String tagName,
129                        String ancestorTagName) {
130
131                Assert.hasText(tagName, "'tagName' must not be empty");
132                Assert.hasText(ancestorTagName, "'ancestorTagName' must not be empty");
133                if (!TagUtils.hasAncestorOfType(tag, ancestorTagClass)) {
134                        throw new IllegalStateException("The '" + tagName +
135                                        "' tag can only be used inside a valid '" + ancestorTagName + "' tag.");
136                }
137        }
138
139}