1 /*
2  * Copyright 2002-2018 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 module hunt.stomp.support.MessageBuilder;
18 
19 import hunt.stomp.support.ErrorMessage;
20 import hunt.stomp.support.GenericMessage;
21 import hunt.stomp.support.MessageHeaderAccessor;
22 
23 import hunt.stomp.Message;
24 import hunt.stomp.MessageChannel;
25 import hunt.stomp.MessageHeaders;
26 
27 import hunt.collection.Map;
28 
29 /**
30  * A builder for creating a {@link GenericMessage}
31  * (or {@link ErrorMessage} if the payload is of type {@link Throwable}).
32  *
33  * @author Arjen Poutsma
34  * @author Mark Fisher
35  * @author Rossen Stoyanchev
36  * @since 4.0
37  * @param (T) the message payload type
38  * @see GenericMessage
39  * @see ErrorMessage
40  */
41 final class MessageBuilder(T) {
42 
43 	private T payload;
44 	
45 	private Message!(T) originalMessage;
46 
47 	private MessageHeaderAccessor headerAccessor;
48 
49 
50 	private this(Message!(T) originalMessage) {
51 		assert(originalMessage, "Message must not be null");
52 		this.payload = originalMessage.getPayload();
53 		this.originalMessage = originalMessage;
54 		this.headerAccessor = new MessageHeaderAccessor(originalMessage);
55 	}
56 
57 	private this(T payload, MessageHeaderAccessor accessor) {
58 		// assert(payload, "Payload must not be null");
59 		assert(accessor, "MessageHeaderAccessor must not be null");
60 		this.payload = payload;
61 		this.originalMessage = null;
62 		this.headerAccessor = accessor;
63 	}
64 
65 
66 	/**
67 	 * Set the message headers to use by providing a {@code MessageHeaderAccessor}.
68 	 * @param accessor the headers to use
69 	 */
70 	MessageBuilder!(T) setHeaders(MessageHeaderAccessor accessor) {
71 		assert(accessor, "MessageHeaderAccessor must not be null");
72 		this.headerAccessor = accessor;
73 		return this;
74 	}
75 
76 	/**
77 	 * Set the value for the given header name. If the provided value is {@code null},
78 	 * the header will be removed.
79 	 */
80 	MessageBuilder!(T) setHeader(string headerName, Object headerValue) {
81 		this.headerAccessor.setHeader(headerName, headerValue);
82 		return this;
83 	}
84 
85 	/**
86 	 * Set the value for the given header name only if the header name is not already
87 	 * associated with a value.
88 	 */
89 	MessageBuilder!(T) setHeaderIfAbsent(string headerName, Object headerValue) {
90 		this.headerAccessor.setHeaderIfAbsent(headerName, headerValue);
91 		return this;
92 	}
93 
94 	/**
95 	 * Removes all headers provided via array of 'headerPatterns'. As the name suggests
96 	 * the array may contain simple matching patterns for header names. Supported pattern
97 	 * styles are: "xxx*", "*xxx", "*xxx*" and "xxx*yyy".
98 	 */
99 	MessageBuilder!(T) removeHeaders(string[] headerPatterns... ) {
100 		this.headerAccessor.removeHeaders(headerPatterns.dup);
101 		return this;
102 	}
103 	/**
104 	 * Remove the value for the given header name.
105 	 */
106 	MessageBuilder!(T) removeHeader(string headerName) {
107 		this.headerAccessor.removeHeader(headerName);
108 		return this;
109 	}
110 
111 	/**
112 	 * Copy the name-value pairs from the provided Map. This operation will overwrite any
113 	 * existing values. Use { {@link #copyHeadersIfAbsent(Map)} to avoid overwriting
114 	 * values. Note that the 'id' and 'timestamp' header values will never be overwritten.
115 	 */
116 	MessageBuilder!(T) copyHeaders(Map!(string, Object) headersToCopy) {
117 		this.headerAccessor.copyHeaders(headersToCopy);
118 		return this;
119 	}
120 
121 	/**
122 	 * Copy the name-value pairs from the provided Map. This operation will <em>not</em>
123 	 * overwrite any existing values.
124 	 */
125 	MessageBuilder!(T) copyHeadersIfAbsent(Map!(string, Object) headersToCopy) {
126 		this.headerAccessor.copyHeadersIfAbsent(headersToCopy);
127 		return this;
128 	}
129 
130 	MessageBuilder!(T) setReplyChannel(MessageChannel replyChannel) {
131 		this.headerAccessor.setReplyChannel(replyChannel);
132 		return this;
133 	}
134 
135 	MessageBuilder!(T) setReplyChannelName(string replyChannelName) {
136 		this.headerAccessor.setReplyChannelName(replyChannelName);
137 		return this;
138 	}
139 
140 	MessageBuilder!(T) setErrorChannel(MessageChannel errorChannel) {
141 		this.headerAccessor.setErrorChannel(errorChannel);
142 		return this;
143 	}
144 
145 	MessageBuilder!(T) setErrorChannelName(string errorChannelName) {
146 		this.headerAccessor.setErrorChannelName(errorChannelName);
147 		return this;
148 	}
149 
150 	
151 	Message!(T) build() {
152 		if (this.originalMessage !is null && !this.headerAccessor.isModified()) {
153 			return this.originalMessage;
154 		}
155 		MessageHeaders headersToUse = this.headerAccessor.toMessageHeaders();
156 		static if(is(T == class) || is(T == interface)) {
157 			Throwable th = cast(Throwable) this.payload;
158 			if (th !is null) {
159 				return cast(Message!(T)) new ErrorMessage(th, headersToUse);
160 			} 
161 		} 
162 		
163 		return new GenericMessage!(T)(this.payload, headersToUse);
164 	}
165 
166 }
167 
168 
169 /**
170 */
171 class MessageHelper {
172 
173 	/**
174 	 * Create a builder for a new {@link Message} instance pre-populated with all of the
175 	 * headers copied from the provided message. The payload of the provided Message will
176 	 * also be used as the payload for the new message.
177 	 * @param message the Message from which the payload and all headers will be copied
178 	 */
179 	static MessageBuilder!(T) fromMessage(T)(Message!(T) message) {
180 		return new MessageBuilder!(T)(message);
181 	}
182 
183 	/**
184 	 * Create a new builder for a message with the given payload.
185 	 * @param payload the payload
186 	 */
187 	static MessageBuilder!(T) withPayload(T)(T payload) {
188 		return new MessageBuilder!(T)(payload, new MessageHeaderAccessor());
189 	}
190 
191 	/**
192 	 * A shortcut factory method for creating a message with the given payload
193 	 * and {@code MessageHeaders}.
194 	 * <p><strong>Note:</strong> the given {@code MessageHeaders} instance is used
195 	 * directly in the new message, i.e. it is not copied.
196 	 * @param payload the payload to use (never {@code null})
197 	 * @param messageHeaders the headers to use (never {@code null})
198 	 * @return the created message
199 	 * @since 4.1
200 	 */
201 	static MessageBase createMessage(T)(Object payload, MessageHeaders messageHeaders) 
202 		if(is(T == class) || is(T == interface)) {
203 		// assert(payload, "Payload must not be null");
204 		assert(messageHeaders, "MessageHeaders must not be null");
205 		Throwable th = cast(Throwable) payload;
206 		if (th !is null) 
207 			return new ErrorMessage(th, messageHeaders);
208 		return new GenericMessage!(T)(payload, messageHeaders);
209 	}
210 
211 	static Message!(T) createMessage(T)(T payload, MessageHeaders messageHeaders) 
212 		if(!is(T == class) && !is(T == interface)) {
213 		// assert(payload, "Payload must not be null");
214 		assert(messageHeaders, "MessageHeaders must not be null");
215 
216 		return new GenericMessage!(T)(payload, messageHeaders);
217 	}
218 }