Index: pkg/shelf/lib/src/message.dart |
diff --git a/pkg/shelf/lib/src/message.dart b/pkg/shelf/lib/src/message.dart |
index 1b2e966c4660b3375519c985a459362257e00408..b4beef89c058496aca2748196e2fa7c84d4d9374 100644 |
--- a/pkg/shelf/lib/src/message.dart |
+++ b/pkg/shelf/lib/src/message.dart |
@@ -43,9 +43,10 @@ abstract class Message { |
/// If [headers] is `null`, it is treated as empty. |
Message(this._body, {Map<String, String> headers, |
Map<String, Object> context}) |
- : this.headers = _getIgnoreCaseMapView(headers), |
- this.context = new pc.UnmodifiableMapView( |
- context == null ? const {} : new Map.from(context)); |
+ : this.headers = new _ShelfUnmodifiableMap<String>(headers, |
+ ignoreKeyCase: true), |
+ this.context = new _ShelfUnmodifiableMap<Object>(context, |
+ ignoreKeyCase: false); |
/// The contents of the content-length field in [headers]. |
/// |
@@ -112,18 +113,86 @@ abstract class Message { |
if (encoding == null) encoding = UTF8; |
return Chain.track(encoding.decodeStream(read())); |
} |
+ |
+ /// Creates a new [Message] by copying existing values and applying specified |
+ /// changes. |
+ Message change({Map<String, String> headers, Map<String, Object> context}); |
+} |
+ |
+/// Returns a [Map] with the values from [original] and the values from |
+/// [updates]. |
+/// |
+/// For keys that are the same between [original] and [updates], the value in |
+/// [updates] is used. |
+/// |
+/// If [updates] is `null` or empty, [original] is returned unchanged. |
+Map updateMap(Map original, Map updates) { |
+ if (updates == null || updates.isEmpty) return original; |
+ |
+ return new Map.from(original) |
+ ..addAll(updates); |
} |
-/// Creates on an unmodifiable map of [headers] with case-insensitive access. |
-Map<String, String> _getIgnoreCaseMapView(Map<String, String> headers) { |
- if (headers == null) return const {}; |
- // TODO(kevmoo) generalize this model with a 'canonical map' to align with |
- // similiar implementation in http pkg [BaseRequest]. |
- var map = new LinkedHashMap<String, String>( |
- equals: (key1, key2) => key1.toLowerCase() == key2.toLowerCase(), |
- hashCode: (key) => key.toLowerCase().hashCode); |
+/// A simple wrapper over [pc.UnmodifiableMapView] which avoids re-wrapping |
+/// itself. |
+class _ShelfUnmodifiableMap<V> extends pc.UnmodifiableMapView<String, V> { |
+ /// If `true`, the keys will have case-insensitive access. |
+ final bool ignoreKeyCase; |
nweiz
2014/04/30 20:21:39
This shouldn't be public.
kevmoo
2014/05/02 16:18:01
Done.
|
- map.addAll(headers); |
+ /// If [source] is a [_ShelfUnmodifiableMap] with matching [ignoreKeyCase], |
+ /// then [source] is returned. |
+ /// |
+ /// If [source] is `null` it is treated like an empty map. |
+ /// |
+ /// If [ignoreKeyCase] is `true`, the keys will have case-insensitive access. |
+ /// |
+ /// [source] is copied to a new [Map] to ensure changes to the paramater value |
+ /// after constructions are not reflected. |
+ factory _ShelfUnmodifiableMap(Map<String, V> source, |
+ {bool ignoreKeyCase: false}) { |
+ if (source is _ShelfUnmodifiableMap<V> && |
+ source.ignoreKeyCase == ignoreKeyCase) { |
+ return source; |
+ } |
+ |
+ if (source == null || source.isEmpty) { |
+ return new _EmptyShelfUnmodifiableMap<V>(ignoreKeyCase); |
+ } |
+ |
+ if (ignoreKeyCase) { |
+ // TODO(kevmoo) generalize this model with a 'canonical map' to align with |
+ // similiar implementation in http pkg [BaseRequest]. |
+ var map = new LinkedHashMap<String, V>( |
+ equals: (key1, key2) => key1.toLowerCase() == key2.toLowerCase(), |
+ hashCode: (key) => key.toLowerCase().hashCode); |
+ |
+ map.addAll(source); |
+ |
+ source = map; |
+ } else { |
+ source = new Map<String, V>.from(source); |
+ } |
+ |
+ return new _ShelfUnmodifiableMap<V>._(source, ignoreKeyCase); |
+ } |
+ |
+ _ShelfUnmodifiableMap._(Map<String, V> source, this.ignoreKeyCase) |
+ : super(source); |
+} |
+ |
+/// An const empty implementation of [_ShelfUnmodifiableMap]. |
+class _EmptyShelfUnmodifiableMap<V> extends pc.DelegatingMap<String, V> |
+ implements _ShelfUnmodifiableMap<V> { |
+ final bool ignoreKeyCase; |
nweiz
2014/04/30 20:21:39
Once [ignoreKeyCase] is no longer public, there's
kevmoo
2014/05/02 16:18:01
Done.
|
+ |
+ /// Returns one of two const values for [_EmptyShelfUnmodifiableMap] depending |
+ /// on the value for [ignoreKeyCase]. |
+ factory _EmptyShelfUnmodifiableMap(bool ignoreKeyCase) { |
+ if (ignoreKeyCase) { |
+ return const _EmptyShelfUnmodifiableMap._(true); |
+ } |
+ return const _EmptyShelfUnmodifiableMap._(false); |
+ } |
- return new pc.UnmodifiableMapView<String, String>(map); |
+ const _EmptyShelfUnmodifiableMap._(this.ignoreKeyCase) : super(const {}); |
} |