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.NativeMessageHeaderAccessor;
18 
19 import hunt.stomp.support.MessageHeaderAccessor;
20 import hunt.stomp.Message;
21 
22 import hunt.collection;
23 import hunt.util.ObjectUtils;
24 
25 // 
26 // import hunt.framework.util.CollectionUtils;
27 // import hunt.framework.util.LinkedMultiValueMap;
28 // import hunt.framework.util.MultiValueMap;
29 // import hunt.util.ObjectUtils;
30 
31 
32 
33 
34 /**
35  * An extension of {@link MessageHeaderAccessor} that also stores and provides read/write
36  * access to message headers from an external source -- e.g. a Spring {@link Message}
37  * created to represent a STOMP message received from a STOMP client or message broker.
38  * Native message headers are kept in a {@code MultiStringsMap} under the key
39  * {@link #NATIVE_HEADERS}.
40  *
41  * <p>This class is not intended for direct use but is rather expected to be used
42  * indirectly through protocol-specific sub-classes such as
43  * {@link hunt.stomp.simp.stomp.StompHeaderAccessor StompHeaderAccessor}.
44  * Such sub-classes may provide factory methods to translate message headers from
45  * an external messaging source (e.g. STOMP) to Spring {@link Message} headers and
46  * reversely to translate Spring {@link Message} headers to a message to send to an
47  * external source.
48  *
49  * @author Rossen Stoyanchev
50  * @since 4.0
51  */
52 class NativeMessageHeaderAccessor : MessageHeaderAccessor {
53 
54 	/**
55 	 * The header name used to store native headers.
56 	 */
57 	enum string NATIVE_HEADERS = "nativeHeaders";
58 
59 
60 	/**
61 	 * A protected constructor to create new headers.
62 	 */
63 	protected this() {
64 		this(cast(MultiStringsMap) null);
65 	}
66 
67 	/**
68 	 * A protected constructor to create new headers.
69 	 * @param nativeHeaders native headers to create the message with (may be {@code null})
70 	 */
71 	protected this(MultiStringsMap nativeHeaders) {
72 		if (nativeHeaders !is null && nativeHeaders.size() > 0) {
73 			setHeader(NATIVE_HEADERS, new LinkedMultiValueMap!(string, string)(nativeHeaders));
74 		}
75 	}
76 
77 	/**
78 	 * A protected constructor accepting the headers of an existing message to copy.
79 	 */
80 	protected this(MessageBase message) {
81 		super(message);
82 		if (message !is null) {
83 			
84 			MultiStringsMap map = cast(MultiStringsMap) getHeader(NATIVE_HEADERS);
85 			if (map !is null) {
86 				// Force removal since setHeader checks for equality
87 				removeHeader(NATIVE_HEADERS);
88 				setHeader(NATIVE_HEADERS, new LinkedMultiValueMap!(string, string)(map));
89 			}
90 		}
91 	}
92 
93 	
94 	
95 	protected MultiStringsMap getNativeHeaders() {
96 		return cast(MultiStringsMap) getHeader(NATIVE_HEADERS);
97 	}
98 
99 	/**
100 	 * Return a copy of the native header values or an empty map.
101 	 */
102 	MultiStringsMap toNativeHeaderMap() {
103 		MultiStringsMap map = getNativeHeaders();
104 		return (map !is null ? new LinkedMultiValueMap!(string, string)(map) : 
105 			Collections.emptyMap!(string, List!(string))());
106 	}
107 
108 	override
109 	void setImmutable() {
110 		if (isMutable()) {
111 			MultiStringsMap map = getNativeHeaders();
112 			if (map !is null) {
113 				// Force removal since setHeader checks for equality
114 				removeHeader(NATIVE_HEADERS);
115 				setHeader(NATIVE_HEADERS, cast(Object)map);
116 				// setHeader(NATIVE_HEADERS, Collections.unmodifiableMap(map));
117 			}
118 			super.setImmutable();
119 		}
120 	}
121 
122 	/**
123 	 * Whether the native header map contains the give header name.
124 	 */
125 	bool containsNativeHeader(string headerName) {
126 		MultiStringsMap map = getNativeHeaders();
127 		return (map !is null && map.containsKey(headerName));
128 	}
129 
130 	/**
131 	 * Return all values for the specified native header.
132 	 * or {@code null} if none.
133 	 */
134 	
135 	List!(string) getNativeHeader(string headerName) {
136 		MultiStringsMap map = getNativeHeaders();
137 		return (map !is null ? map.get(headerName) : null);
138 	}
139 
140 	/**
141 	 * Return the first value for the specified native header,
142 	 * or {@code null} if none.
143 	 */
144 	
145 	string getFirstNativeHeader(string headerName) {
146 		MultiStringsMap map = getNativeHeaders();
147 		if (map !is null) {
148 			List!(string) values = map.get(headerName);
149 			if (values !is null) {
150 				return values.get(0);
151 			}
152 		}
153 		return null;
154 	}
155 
156 	/**
157 	 * Set the specified native header value replacing existing values.
158 	 */
159 	void setNativeHeader(string name, string value) {
160 		assert(isMutable(), "Already immutable");
161 		MultiStringsMap map = getNativeHeaders();
162 		if (value is null) {
163 			if (map !is null && map.get(name) !is null) {
164 				setModified(true);
165 				map.remove(name);
166 			}
167 			return;
168 		}
169 		if (map is null) {
170 			map = new LinkedMultiValueMap!(string, string)(4);
171 			setHeader(NATIVE_HEADERS, cast(Object)map);
172 		}
173 		List!(string) values = new LinkedList!(string)();
174 		values.add(value);
175 		if (!ObjectUtils.nullSafeEquals(cast(Object)values, getHeader(name))) {
176 			setModified(true);
177 			map.put(name, values);
178 		}
179 	}
180 
181 	/**
182 	 * Add the specified native header value to existing values.
183 	 */
184 	void addNativeHeader(string name, string value) {
185 		assert(isMutable(), "Already immutable");
186 		if (value is null) {
187 			return;
188 		}
189 		MultiStringsMap nativeHeaders = getNativeHeaders();
190 		if (nativeHeaders is null) {
191 			nativeHeaders = new LinkedMultiValueMap!(string, string)(4);
192 			setHeader(NATIVE_HEADERS, cast(Object)nativeHeaders);
193 		}
194 		List!(string) values = nativeHeaders.computeIfAbsent(name, k => new LinkedList!(string)());
195 		values.add(value);
196 		setModified(true);
197 	}
198 
199 	void addNativeHeaders(MultiValueMap!(string, string) headers) {
200 		if (headers is null) {
201 			return;
202 		}
203         foreach(string key, List!(string) values; headers) {
204             foreach(string value; values) {
205                 addNativeHeader(key, value);
206             }
207         }
208 	}
209 
210 	
211 	List!(string) removeNativeHeader(string name) {
212 		assert(isMutable(), "Already immutable");
213 		MultiStringsMap nativeHeaders = getNativeHeaders();
214 		if (nativeHeaders is null) {
215 			return null;
216 		}
217 		return nativeHeaders.remove(name);
218 	}
219 
220 	
221 	
222 	static string getFirstNativeHeader(string headerName, Map!(string, Object) headers) {
223 		MultiStringsMap map = cast(MultiStringsMap) headers.get(NATIVE_HEADERS);
224 		if (map !is null) {
225 			List!(string) values = map.get(headerName);
226 			if (values !is null) {
227 				return values.get(0);
228 			}
229 		}
230 		return null;
231 	}
232 
233 }