Merge pull request #1263 from abidrahmank:pyCLAHE_24
[profile/ivi/opencv.git] / samples / winrt / ImageManipulations / common / suspensionmanager.cpp
1 //*********************************************************
2 //
3 // Copyright (c) Microsoft. All rights reserved.
4 // THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
5 // ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
6 // IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
7 // PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
8 //
9 //*********************************************************
10
11 //
12 // SuspensionManager.cpp
13 // Implementation of the SuspensionManager class
14 //
15
16 #include "pch.h"
17 #include "SuspensionManager.h"
18
19 #include <collection.h>
20 #include <algorithm>
21
22 using namespace SDKSample::Common;
23
24 using namespace Concurrency;
25 using namespace Platform;
26 using namespace Platform::Collections;
27 using namespace Windows::Foundation;
28 using namespace Windows::Foundation::Collections;
29 using namespace Windows::Storage;
30 using namespace Windows::Storage::FileProperties;
31 using namespace Windows::Storage::Streams;
32 using namespace Windows::UI::Xaml;
33 using namespace Windows::UI::Xaml::Controls;
34 using namespace Windows::UI::Xaml::Interop;
35
36 namespace
37 {
38     Map<String^, Object^>^ _sessionState = ref new Map<String^, Object^>();
39     String^ sessionStateFilename = "_sessionState.dat";
40
41     // Forward declarations for object object read / write support
42     void WriteObject(Windows::Storage::Streams::DataWriter^ writer, Platform::Object^ object);
43     Platform::Object^ ReadObject(Windows::Storage::Streams::DataReader^ reader);
44 }
45
46 /// <summary>
47 /// Provides access to global session state for the current session.  This state is serialized by
48 /// <see cref="SaveAsync"/> and restored by <see cref="RestoreAsync"/> which require values to be
49 /// one of the following: boxed values including integers, floating-point singles and doubles,
50 /// wide characters, boolean, Strings and Guids, or Map<String^, Object^> where map values are
51 /// subject to the same constraints.  Session state should be as compact as possible.
52 /// </summary>
53 IMap<String^, Object^>^ SuspensionManager::SessionState::get(void)
54 {
55     return _sessionState;
56 }
57
58 /// <summary>
59 /// Wrap a WeakReference as a reference object for use in a collection.
60 /// </summary>
61 private ref class WeakFrame sealed
62 {
63 private:
64     WeakReference _frameReference;
65
66 internal:
67     WeakFrame(Frame^ frame) { _frameReference = frame; }
68     property Frame^ ResolvedFrame
69     {
70         Frame^ get(void) { return _frameReference.Resolve<Frame>(); }
71     };
72 };
73
74 namespace
75 {
76     std::vector<WeakFrame^> _registeredFrames;
77     DependencyProperty^ FrameSessionStateKeyProperty =
78         DependencyProperty::RegisterAttached("_FrameSessionStateKeyProperty",
79         TypeName(String::typeid), TypeName(SuspensionManager::typeid), nullptr);
80     DependencyProperty^ FrameSessionStateProperty =
81         DependencyProperty::RegisterAttached("_FrameSessionStateProperty",
82         TypeName(IMap<String^, Object^>::typeid), TypeName(SuspensionManager::typeid), nullptr);
83 }
84
85 /// <summary>
86 /// Registers a <see cref="Frame"/> instance to allow its navigation history to be saved to
87 /// and restored from <see cref="SessionState"/>.  Frames should be registered once
88 /// immediately after creation if they will participate in session state management.  Upon
89 /// registration if state has already been restored for the specified key
90 /// the navigation history will immediately be restored.  Subsequent invocations of
91 /// <see cref="RestoreAsync(String)"/> will also restore navigation history.
92 /// </summary>
93 /// <param name="frame">An instance whose navigation history should be managed by
94 /// <see cref="SuspensionManager"/></param>
95 /// <param name="sessionStateKey">A unique key into <see cref="SessionState"/> used to
96 /// store navigation-related information.</param>
97 void SuspensionManager::RegisterFrame(Frame^ frame, String^ sessionStateKey)
98 {
99     if (frame->GetValue(FrameSessionStateKeyProperty) != nullptr)
100     {
101         throw ref new FailureException("Frames can only be registered to one session state key");
102     }
103
104     if (frame->GetValue(FrameSessionStateProperty) != nullptr)
105     {
106         throw ref new FailureException("Frames must be either be registered before accessing frame session state, or not registered at all");
107     }
108
109     // Use a dependency property to associate the session key with a frame, and keep a list of frames whose
110     // navigation state should be managed
111     frame->SetValue(FrameSessionStateKeyProperty, sessionStateKey);
112     _registeredFrames.insert(_registeredFrames.begin(), ref new WeakFrame(frame));
113
114     // Check to see if navigation state can be restored
115     RestoreFrameNavigationState(frame);
116 }
117
118 /// <summary>
119 /// Disassociates a <see cref="Frame"/> previously registered by <see cref="RegisterFrame"/>
120 /// from <see cref="SessionState"/>.  Any navigation state previously captured will be
121 /// removed.
122 /// </summary>
123 /// <param name="frame">An instance whose navigation history should no longer be
124 /// managed.</param>
125 void SuspensionManager::UnregisterFrame(Frame^ frame)
126 {
127     // Remove session state and remove the frame from the list of frames whose navigation
128     // state will be saved (along with any weak references that are no longer reachable)
129     auto key = safe_cast<String^>(frame->GetValue(FrameSessionStateKeyProperty));
130     if (SessionState->HasKey(key)) SessionState->Remove(key);
131     _registeredFrames.erase(
132         std::remove_if(_registeredFrames.begin(), _registeredFrames.end(), [=](WeakFrame^& e)
133         {
134             auto testFrame = e->ResolvedFrame;
135             return testFrame == nullptr || testFrame == frame;
136         }),
137         _registeredFrames.end()
138     );
139 }
140
141 /// <summary>
142 /// Provides storage for session state associated with the specified <see cref="Frame"/>.
143 /// Frames that have been previously registered with <see cref="RegisterFrame"/> have
144 /// their session state saved and restored automatically as a part of the global
145 /// <see cref="SessionState"/>.  Frames that are not registered have transient state
146 /// that can still be useful when restoring pages that have been discarded from the
147 /// navigation cache.
148 /// </summary>
149 /// <remarks>Apps may choose to rely on <see cref="LayoutAwarePage"/> to manage
150 /// page-specific state instead of working with frame session state directly.</remarks>
151 /// <param name="frame">The instance for which session state is desired.</param>
152 /// <returns>A collection of state subject to the same serialization mechanism as
153 /// <see cref="SessionState"/>.</returns>
154 IMap<String^, Object^>^ SuspensionManager::SessionStateForFrame(Frame^ frame)
155 {
156     auto frameState = safe_cast<IMap<String^, Object^>^>(frame->GetValue(FrameSessionStateProperty));
157
158     if (frameState == nullptr)
159     {
160         auto frameSessionKey = safe_cast<String^>(frame->GetValue(FrameSessionStateKeyProperty));
161         if (frameSessionKey != nullptr)
162         {
163             // Registered frames reflect the corresponding session state
164             if (!_sessionState->HasKey(frameSessionKey))
165             {
166                 _sessionState->Insert(frameSessionKey, ref new Map<String^, Object^>());
167             }
168             frameState = safe_cast<IMap<String^, Object^>^>(_sessionState->Lookup(frameSessionKey));
169         }
170         else
171         {
172             // Frames that aren't registered have transient state
173             frameState = ref new Map<String^, Object^>();
174         }
175         frame->SetValue(FrameSessionStateProperty, frameState);
176     }
177     return frameState;
178 }
179
180 void SuspensionManager::RestoreFrameNavigationState(Frame^ frame)
181 {
182     auto frameState = SessionStateForFrame(frame);
183     if (frameState->HasKey("Navigation"))
184     {
185         frame->SetNavigationState(safe_cast<String^>(frameState->Lookup("Navigation")));
186     }
187 }
188
189 void SuspensionManager::SaveFrameNavigationState(Frame^ frame)
190 {
191     auto frameState = SessionStateForFrame(frame);
192     frameState->Insert("Navigation", frame->GetNavigationState());
193 }
194
195 /// <summary>
196 /// Save the current <see cref="SessionState"/>.  Any <see cref="Frame"/> instances
197 /// registered with <see cref="RegisterFrame"/> will also preserve their current
198 /// navigation stack, which in turn gives their active <see cref="Page"/> an opportunity
199 /// to save its state.
200 /// </summary>
201 /// <returns>An asynchronous task that reflects when session state has been saved.</returns>
202 task<void> SuspensionManager::SaveAsync(void)
203 {
204     // Save the navigation state for all registered frames
205     for (auto&& weakFrame : _registeredFrames)
206     {
207         auto frame = weakFrame->ResolvedFrame;
208         if (frame != nullptr) SaveFrameNavigationState(frame);
209     }
210
211     // Serialize the session state synchronously to avoid asynchronous access to shared
212     // state
213     auto sessionData = ref new InMemoryRandomAccessStream();
214     auto sessionDataWriter = ref new DataWriter(sessionData->GetOutputStreamAt(0));
215     WriteObject(sessionDataWriter, _sessionState);
216
217     // Once session state has been captured synchronously, begin the asynchronous process
218     // of writing the result to disk
219     return task<unsigned int>(sessionDataWriter->StoreAsync()).then([=](unsigned int)
220     {
221         return sessionDataWriter->FlushAsync();
222     }).then([=](bool flushSucceeded)
223     {
224         (void)flushSucceeded; // Unused parameter
225         return ApplicationData::Current->LocalFolder->CreateFileAsync(sessionStateFilename,
226             CreationCollisionOption::ReplaceExisting);
227     }).then([=](StorageFile^ createdFile)
228     {
229         return createdFile->OpenAsync(FileAccessMode::ReadWrite);
230     }).then([=](IRandomAccessStream^ newStream)
231     {
232         return RandomAccessStream::CopyAndCloseAsync(
233             sessionData->GetInputStreamAt(0), newStream->GetOutputStreamAt(0));
234     }).then([=](UINT64 copiedBytes)
235     {
236         (void)copiedBytes; // Unused parameter
237         return;
238     });
239 }
240
241 /// <summary>
242 /// Restores previously saved <see cref="SessionState"/>.  Any <see cref="Frame"/> instances
243 /// registered with <see cref="RegisterFrame"/> will also restore their prior navigation
244 /// state, which in turn gives their active <see cref="Page"/> an opportunity restore its
245 /// state.
246 /// </summary>
247 /// <param name="version">A version identifer compared to the session state to prevent
248 /// incompatible versions of session state from reaching app code.  Saved state with a
249 /// different version will be ignored, resulting in an empty <see cref="SessionState"/>
250 /// dictionary.</param>
251 /// <returns>An asynchronous task that reflects when session state has been read.  The
252 /// content of <see cref="SessionState"/> should not be relied upon until this task
253 /// completes.</returns>
254 task<void> SuspensionManager::RestoreAsync(void)
255 {
256     _sessionState->Clear();
257
258     task<StorageFile^> getFileTask(ApplicationData::Current->LocalFolder->GetFileAsync(sessionStateFilename));
259     return getFileTask.then([=](StorageFile^ stateFile)
260     {
261         task<BasicProperties^> getBasicPropertiesTask(stateFile->GetBasicPropertiesAsync());
262         return getBasicPropertiesTask.then([=](BasicProperties^ stateFileProperties)
263         {
264             auto size = unsigned int(stateFileProperties->Size);
265             if (size != stateFileProperties->Size) throw ref new FailureException("Session state larger than 4GB");
266             task<IRandomAccessStreamWithContentType^> openReadTask(stateFile->OpenReadAsync());
267             return openReadTask.then([=](IRandomAccessStreamWithContentType^ stateFileStream)
268             {
269                 auto stateReader = ref new DataReader(stateFileStream);
270                 return task<unsigned int>(stateReader->LoadAsync(size)).then([=](unsigned int bytesRead)
271                 {
272                     (void)bytesRead; // Unused parameter
273                     // Deserialize the Session State
274                     Object^ content = ReadObject(stateReader);
275                     _sessionState = (Map<String^, Object^>^)content;
276
277                     // Restore any registered frames to their saved state
278                     for (auto&& weakFrame : _registeredFrames)
279                     {
280                         auto frame = weakFrame->ResolvedFrame;
281                         if (frame != nullptr)
282                         {
283                             frame->ClearValue(FrameSessionStateProperty);
284                             RestoreFrameNavigationState(frame);
285                         }
286                     }
287                 }, task_continuation_context::use_current());
288             });
289         });
290     });
291 }
292
293 #pragma region Object serialization for a known set of types
294
295 namespace
296 {
297     // Codes used for identifying serialized types
298     enum StreamTypes {
299         NullPtrType = 0,
300
301         // Supported IPropertyValue types
302         UInt8Type, UInt16Type, UInt32Type, UInt64Type, Int16Type, Int32Type, Int64Type,
303         SingleType, DoubleType, BooleanType, Char16Type, GuidType, StringType,
304
305         // Additional supported types
306         StringToObjectMapType,
307
308         // Marker values used to ensure stream integrity
309         MapEndMarker
310     };
311
312     void WriteString(DataWriter^ writer, String^ string)
313     {
314         writer->WriteByte(StringType);
315         writer->WriteUInt32(writer->MeasureString(string));
316         writer->WriteString(string);
317     }
318
319     void WriteProperty(DataWriter^ writer, IPropertyValue^ propertyValue)
320     {
321         switch (propertyValue->Type)
322         {
323         case PropertyType::UInt8:
324             writer->WriteByte(UInt8Type);
325             writer->WriteByte(propertyValue->GetUInt8());
326             return;
327         case PropertyType::UInt16:
328             writer->WriteByte(UInt16Type);
329             writer->WriteUInt16(propertyValue->GetUInt16());
330             return;
331         case PropertyType::UInt32:
332             writer->WriteByte(UInt32Type);
333             writer->WriteUInt32(propertyValue->GetUInt32());
334             return;
335         case PropertyType::UInt64:
336             writer->WriteByte(UInt64Type);
337             writer->WriteUInt64(propertyValue->GetUInt64());
338             return;
339         case PropertyType::Int16:
340             writer->WriteByte(Int16Type);
341             writer->WriteUInt16(propertyValue->GetInt16());
342             return;
343         case PropertyType::Int32:
344             writer->WriteByte(Int32Type);
345             writer->WriteUInt32(propertyValue->GetInt32());
346             return;
347         case PropertyType::Int64:
348             writer->WriteByte(Int64Type);
349             writer->WriteUInt64(propertyValue->GetInt64());
350             return;
351         case PropertyType::Single:
352             writer->WriteByte(SingleType);
353             writer->WriteSingle(propertyValue->GetSingle());
354             return;
355         case PropertyType::Double:
356             writer->WriteByte(DoubleType);
357             writer->WriteDouble(propertyValue->GetDouble());
358             return;
359         case PropertyType::Boolean:
360             writer->WriteByte(BooleanType);
361             writer->WriteBoolean(propertyValue->GetBoolean());
362             return;
363         case PropertyType::Char16:
364             writer->WriteByte(Char16Type);
365             writer->WriteUInt16(propertyValue->GetChar16());
366             return;
367         case PropertyType::Guid:
368             writer->WriteByte(GuidType);
369             writer->WriteGuid(propertyValue->GetGuid());
370             return;
371         case PropertyType::String:
372             WriteString(writer, propertyValue->GetString());
373             return;
374         default:
375             throw ref new InvalidArgumentException("Unsupported property type");
376         }
377     }
378
379     void WriteStringToObjectMap(DataWriter^ writer, IMap<String^, Object^>^ map)
380     {
381         writer->WriteByte(StringToObjectMapType);
382         writer->WriteUInt32(map->Size);
383         for (auto&& pair : map)
384         {
385             WriteObject(writer, pair->Key);
386             WriteObject(writer, pair->Value);
387         }
388         writer->WriteByte(MapEndMarker);
389     }
390
391     void WriteObject(DataWriter^ writer, Object^ object)
392     {
393         if (object == nullptr)
394         {
395             writer->WriteByte(NullPtrType);
396             return;
397         }
398
399         auto propertyObject = dynamic_cast<IPropertyValue^>(object);
400         if (propertyObject != nullptr)
401         {
402             WriteProperty(writer, propertyObject);
403             return;
404         }
405
406         auto mapObject = dynamic_cast<IMap<String^, Object^>^>(object);
407         if (mapObject != nullptr)
408         {
409             WriteStringToObjectMap(writer, mapObject);
410             return;
411         }
412
413         throw ref new InvalidArgumentException("Unsupported data type");
414     }
415
416     String^ ReadString(DataReader^ reader)
417     {
418         int length = reader->ReadUInt32();
419         String^ string = reader->ReadString(length);
420         return string;
421     }
422
423     IMap<String^, Object^>^ ReadStringToObjectMap(DataReader^ reader)
424     {
425         auto map = ref new Map<String^, Object^>();
426         auto size = reader->ReadUInt32();
427         for (unsigned int index = 0; index < size; index++)
428         {
429             auto key = safe_cast<String^>(ReadObject(reader));
430             auto value = ReadObject(reader);
431             map->Insert(key, value);
432         }
433         if (reader->ReadByte() != MapEndMarker)
434         {
435             throw ref new InvalidArgumentException("Invalid stream");
436         }
437         return map;
438     }
439
440     Object^ ReadObject(DataReader^ reader)
441     {
442         auto type = reader->ReadByte();
443         switch (type)
444         {
445         case NullPtrType:
446             return nullptr;
447         case UInt8Type:
448             return reader->ReadByte();
449         case UInt16Type:
450             return reader->ReadUInt16();
451         case UInt32Type:
452             return reader->ReadUInt32();
453         case UInt64Type:
454             return reader->ReadUInt64();
455         case Int16Type:
456             return reader->ReadInt16();
457         case Int32Type:
458             return reader->ReadInt32();
459         case Int64Type:
460             return reader->ReadInt64();
461         case SingleType:
462             return reader->ReadSingle();
463         case DoubleType:
464             return reader->ReadDouble();
465         case BooleanType:
466             return reader->ReadBoolean();
467         case Char16Type:
468             return (char16_t)reader->ReadUInt16();
469         case GuidType:
470             return reader->ReadGuid();
471         case StringType:
472             return ReadString(reader);
473         case StringToObjectMapType:
474             return ReadStringToObjectMap(reader);
475         default:
476             throw ref new InvalidArgumentException("Unsupported property type");
477         }
478     }
479 }
480
481 #pragma endregion