001/*
002 * Copyright 2002-2019 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.messaging.handler;
018
019import java.util.ArrayList;
020import java.util.Arrays;
021import java.util.List;
022import java.util.stream.Collectors;
023
024import org.springframework.lang.Nullable;
025import org.springframework.messaging.Message;
026import org.springframework.util.Assert;
027
028/**
029 * Composite {@link MessageCondition} that delegates to other message conditions.
030 *
031 * <p>For {@link #combine} and {@link #compareTo} it is expected that the "other"
032 * composite contains the same number, type, and order of message conditions.
033 *
034 * @author Rossen Stoyanchev
035 * @since 5.2
036 */
037public class CompositeMessageCondition implements MessageCondition<CompositeMessageCondition> {
038
039        private final List<MessageCondition<?>> messageConditions;
040
041
042        public CompositeMessageCondition(MessageCondition<?>... messageConditions) {
043                this(Arrays.asList(messageConditions));
044        }
045
046        private CompositeMessageCondition(List<MessageCondition<?>> messageConditions) {
047                Assert.notEmpty(messageConditions, "No message conditions");
048                this.messageConditions = messageConditions;
049        }
050
051
052        public List<MessageCondition<?>> getMessageConditions() {
053                return this.messageConditions;
054        }
055
056        @SuppressWarnings("unchecked")
057        public <T extends MessageCondition<T>> T getCondition(Class<T> messageConditionType) {
058                for (MessageCondition<?> condition : this.messageConditions) {
059                        if (messageConditionType.isAssignableFrom(condition.getClass())) {
060                                return (T) condition;
061                        }
062                }
063                throw new IllegalStateException("No condition of type: " + messageConditionType);
064        }
065
066
067        @Override
068        public CompositeMessageCondition combine(CompositeMessageCondition other) {
069                checkCompatible(other);
070                List<MessageCondition<?>> result = new ArrayList<>(this.messageConditions.size());
071                for (int i = 0; i < this.messageConditions.size(); i++) {
072                        result.add(combine(getMessageConditions().get(i), other.getMessageConditions().get(i)));
073                }
074                return new CompositeMessageCondition(result);
075        }
076
077        @SuppressWarnings("unchecked")
078        private <T extends MessageCondition<T>> T combine(MessageCondition<?> first, MessageCondition<?> second) {
079                return ((T) first).combine((T) second);
080        }
081
082        @Override
083        public CompositeMessageCondition getMatchingCondition(Message<?> message) {
084                List<MessageCondition<?>> result = new ArrayList<>(this.messageConditions.size());
085                for (MessageCondition<?> condition : this.messageConditions) {
086                        MessageCondition<?> matchingCondition = (MessageCondition<?>) condition.getMatchingCondition(message);
087                        if (matchingCondition == null) {
088                                return null;
089                        }
090                        result.add(matchingCondition);
091                }
092                return new CompositeMessageCondition(result);
093        }
094
095        @Override
096        public int compareTo(CompositeMessageCondition other, Message<?> message) {
097                checkCompatible(other);
098                List<MessageCondition<?>> otherConditions = other.getMessageConditions();
099                for (int i = 0; i < this.messageConditions.size(); i++) {
100                        int result = compare (this.messageConditions.get(i), otherConditions.get(i), message);
101                        if (result != 0) {
102                                return result;
103                        }
104                }
105                return 0;
106        }
107
108        @SuppressWarnings("unchecked")
109        private <T extends MessageCondition<T>> int compare(
110                        MessageCondition<?> first, MessageCondition<?> second, Message<?> message) {
111
112                return ((T) first).compareTo((T) second, message);
113        }
114
115        private void checkCompatible(CompositeMessageCondition other) {
116                List<MessageCondition<?>> others = other.getMessageConditions();
117                for (int i = 0; i < this.messageConditions.size(); i++) {
118                        if (i < others.size()) {
119                                if (this.messageConditions.get(i).getClass().equals(others.get(i).getClass())) {
120                                        continue;
121                                }
122                        }
123                        throw new IllegalArgumentException("Mismatched CompositeMessageCondition: " +
124                                        this.messageConditions + " vs " + others);
125                }
126        }
127
128
129        @Override
130        public boolean equals(@Nullable Object other) {
131                if (this == other) {
132                        return true;
133                }
134                if (!(other instanceof CompositeMessageCondition)) {
135                        return false;
136                }
137                CompositeMessageCondition otherComposite = (CompositeMessageCondition) other;
138                checkCompatible(otherComposite);
139                List<MessageCondition<?>> otherConditions = otherComposite.getMessageConditions();
140                for (int i = 0; i < this.messageConditions.size(); i++) {
141                        if (!this.messageConditions.get(i).equals(otherConditions.get(i))) {
142                                return false;
143                        }
144                }
145                return true;
146        }
147
148        @Override
149        public int hashCode() {
150                int hashCode = 0;
151                for (MessageCondition<?> condition : this.messageConditions) {
152                        hashCode += condition.hashCode() * 31;
153                }
154                return hashCode;
155        }
156
157        @Override
158        public String toString() {
159                return this.messageConditions.stream().map(Object::toString).collect(Collectors.joining(",", "{", "}"));
160        }
161
162}