001/*
002 * Copyright 2002-2014 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.servlet.mvc.condition;
018
019import java.util.Collection;
020import java.util.Collections;
021import javax.servlet.http.HttpServletRequest;
022
023/**
024 * A holder for a {@link RequestCondition} useful when the type of the request
025 * condition is not known ahead of time, e.g. custom condition. Since this
026 * class is also an implementation of {@code RequestCondition}, effectively it
027 * decorates the held request condition and allows it to be combined and compared
028 * with other request conditions in a type and null safe way.
029 *
030 * <p>When two {@code RequestConditionHolder} instances are combined or compared
031 * with each other, it is expected the conditions they hold are of the same type.
032 * If they are not, a {@link ClassCastException} is raised.
033 *
034 * @author Rossen Stoyanchev
035 * @since 3.1
036 */
037public final class RequestConditionHolder extends AbstractRequestCondition<RequestConditionHolder> {
038
039        private final RequestCondition<Object> condition;
040
041
042        /**
043         * Create a new holder to wrap the given request condition.
044         * @param requestCondition the condition to hold, may be {@code null}
045         */
046        @SuppressWarnings("unchecked")
047        public RequestConditionHolder(RequestCondition<?> requestCondition) {
048                this.condition = (RequestCondition<Object>) requestCondition;
049        }
050
051
052        /**
053         * Return the held request condition, or {@code null} if not holding one.
054         */
055        public RequestCondition<?> getCondition() {
056                return this.condition;
057        }
058
059        @Override
060        protected Collection<?> getContent() {
061                return (this.condition != null ? Collections.singleton(this.condition) : Collections.emptyList());
062        }
063
064        @Override
065        protected String getToStringInfix() {
066                return " ";
067        }
068
069        /**
070         * Combine the request conditions held by the two RequestConditionHolder
071         * instances after making sure the conditions are of the same type.
072         * Or if one holder is empty, the other holder is returned.
073         */
074        @Override
075        public RequestConditionHolder combine(RequestConditionHolder other) {
076                if (this.condition == null && other.condition == null) {
077                        return this;
078                }
079                else if (this.condition == null) {
080                        return other;
081                }
082                else if (other.condition == null) {
083                        return this;
084                }
085                else {
086                        assertEqualConditionTypes(other);
087                        RequestCondition<?> combined = (RequestCondition<?>) this.condition.combine(other.condition);
088                        return new RequestConditionHolder(combined);
089                }
090        }
091
092        /**
093         * Ensure the held request conditions are of the same type.
094         */
095        private void assertEqualConditionTypes(RequestConditionHolder other) {
096                Class<?> clazz = this.condition.getClass();
097                Class<?> otherClazz = other.condition.getClass();
098                if (!clazz.equals(otherClazz)) {
099                        throw new ClassCastException("Incompatible request conditions: " + clazz + " and " + otherClazz);
100                }
101        }
102
103        /**
104         * Get the matching condition for the held request condition wrap it in a
105         * new RequestConditionHolder instance. Or otherwise if this is an empty
106         * holder, return the same holder instance.
107         */
108        @Override
109        public RequestConditionHolder getMatchingCondition(HttpServletRequest request) {
110                if (this.condition == null) {
111                        return this;
112                }
113                RequestCondition<?> match = (RequestCondition<?>) this.condition.getMatchingCondition(request);
114                return (match != null ? new RequestConditionHolder(match) : null);
115        }
116
117        /**
118         * Compare the request conditions held by the two RequestConditionHolder
119         * instances after making sure the conditions are of the same type.
120         * Or if one holder is empty, the other holder is preferred.
121         */
122        @Override
123        public int compareTo(RequestConditionHolder other, HttpServletRequest request) {
124                if (this.condition == null && other.condition == null) {
125                        return 0;
126                }
127                else if (this.condition == null) {
128                        return 1;
129                }
130                else if (other.condition == null) {
131                        return -1;
132                }
133                else {
134                        assertEqualConditionTypes(other);
135                        return this.condition.compareTo(other.condition, request);
136                }
137        }
138
139}