Audacity 3.2.0
Journal.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 Journal.cpp
6
7 Paul Licameli
8
9*******************************************************************//*******************************************************************/
15
16#include "Journal.h"
17#include "JournalOutput.h"
18#include "JournalRegistry.h"
19
20#include <algorithm>
21#include <wx/app.h>
22#include <wx/filename.h>
23
24#include "MemoryX.h"
25#include "Prefs.h"
26
27namespace Journal {
28
29namespace {
30
31wxString sFileNameIn;
32wxTextFile sFileIn;
33
34wxString sLine;
35// Invariant: the input file has not been opened, or else sLineNumber counts
36// the number of lines consumed by the tokenizer
37int sLineNumber = -1;
38
39BoolSetting JournalEnabled{ L"/Journal/Enabled", false };
40
41inline void NextIn()
42{
43 if ( !sFileIn.Eof() ) {
44 sLine = sFileIn.GetNextLine();
46 }
47}
48
50{
51 wxArrayStringEx tokens;
53 for ( ; !sFileIn.Eof(); NextIn() ) {
54 if ( sLine.StartsWith( CommentCharacter ) )
55 continue;
56
57 tokens = wxSplit( sLine, SeparatorCharacter, EscapeCharacter );
58 if ( tokens.empty() )
59 // Ignore blank lines
60 continue;
61
62 break;
63 }
64 return tokens;
65}
66
67constexpr auto VersionToken = wxT("Version");
68
69// Numbers identifying the journal format version
71 1
72};
73
74wxString VersionString()
75{
76 wxString result;
77 for ( auto number : journalVersionNumbers ) {
78 auto str = wxString::Format( "%d", number );
79 result += ( result.empty() ? str : ( '.' + str ) );
80 }
81 return result;
82}
83
85bool VersionCheck( const wxString &value )
86{
87 auto strings = wxSplit( value, '.' );
88 std::vector<int> numbers;
89 for ( auto &string : strings ) {
90 long value;
91 if ( !string.ToCLong( &value ) )
92 return false;
93 numbers.push_back( value );
94 }
95 // OK if the static version number is not less than the given value
96 // Maybe in the future there will be a compatibility break
97 return !std::lexicographical_compare(
99 numbers.begin(), numbers.end() );
100}
101
102}
103
105{
106 // If the exception is ever constructed, cause nonzero program exit code
107 SetError();
108}
109
111
113{
114 // Simulate the application Exit menu item
115 wxCommandEvent evt{ wxEVT_MENU, wxID_EXIT };
116 wxTheApp->AddPendingEvent( evt );
117}
118
120{
121 return JournalEnabled.Read();
122}
123
124bool SetRecordEnabled(bool value)
125{
126 auto result = JournalEnabled.Write(value);
127 gPrefs->Flush();
128 return result;
129}
130
132{
133 return sFileIn.IsOpened();
134}
135
136void SetInputFileName(const wxString &path)
137{
138 sFileNameIn = path;
139}
140
141bool Begin( const FilePath &dataDir )
142{
143 if ( !GetError() && !sFileNameIn.empty() ) {
144 wxFileName fName{ sFileNameIn };
145 fName.MakeAbsolute( dataDir );
146 const auto path = fName.GetFullPath();
147 sFileIn.Open( path );
148 if ( !sFileIn.IsOpened() )
149 SetError();
150 else {
151 sLine = sFileIn.GetFirstLine();
152 sLineNumber = 0;
153
154 auto tokens = PeekTokens();
155 NextIn();
156 if( !(
157 tokens.size() == 2 &&
158 tokens[0] == VersionToken &&
159 VersionCheck( tokens[1] )
160 ) )
161 SetError();
162 }
163 }
164
165 if ( !GetError() && RecordEnabled() ) {
166 wxFileName fName{ dataDir, "journal", "txt" };
167 const auto path = fName.GetFullPath();
168 if ( !OpenOut( path ) )
169 SetError();
170 else {
171 // Generate a header
172 Comment( wxString::Format(
173 wxT("Journal recorded by %s on %s")
174 , wxGetUserName()
175 , wxDateTime::Now().Format()
176 ) );
178 }
179 }
180
181 // Call other registered initialization steps
182 for (auto &initializer : GetInitializers()) {
183 if (initializer && !initializer()) {
184 SetError();
185 break;
186 }
187 }
188
189 return !GetError();
190}
191
193{
194 auto result = PeekTokens();
195 if ( !result.empty() ) {
196 NextIn();
197 return result;
198 }
199 throw SyncException{};
200}
201
203{
204 if ( GetError() )
205 // Don't repeatedly indicate error
206 // Do nothing
207 return false;
208
209 if ( !IsReplaying() )
210 return false;
211
212 // This will throw if no lines remain. A proper journal should exit the
213 // program before that happens.
214 auto words = GetTokens();
215
216 // Lookup dispatch function by the first field of the line
217 auto &table = GetDictionary();
218 auto &name = words[0];
219 auto iter = table.find( name );
220 if ( iter == table.end() )
221 throw SyncException{};
222
223 // Pass all the fields including the command name to the function
224 if ( !iter->second( words ) )
225 throw SyncException{};
226
227 return true;
228}
229
230void Sync( const wxString &string )
231{
232 if ( IsRecording() || IsReplaying() ) {
233 if ( IsRecording() )
234 Output( string );
235 if ( IsReplaying() ) {
236 if ( sFileIn.Eof() || sLine != string )
237 throw SyncException{};
238 NextIn();
239 }
240 }
241}
242
243void Sync( const wxArrayString &strings )
244{
245 if ( IsRecording() || IsReplaying() ) {
246 auto string = ::wxJoin( strings, SeparatorCharacter, EscapeCharacter );
247 Sync( string );
248 }
249}
250
251void Sync( std::initializer_list< const wxString > strings )
252{
253 return Sync( wxArrayStringEx( strings ) );
254}
255
257 const wxString &string, const InteractiveAction &action )
258{
259 // Special journal word
260 Sync(string);
261
262 // Then read or write the return value on another journal line
263 if ( IsReplaying() ) {
264 auto tokens = GetTokens();
265 if ( tokens.size() == 1 ) {
266 try {
267 std::wstring str{ tokens[0].wc_str() };
268 size_t length = 0;
269 auto result = std::stoi(str, &length);
270 if (length == str.length()) {
271 if (IsRecording())
272 Journal::Output( std::to_wstring(result) );
273 return result;
274 }
275 }
276 catch ( const std::exception& ) {}
277 }
278 throw SyncException{};
279 }
280 else {
281 auto result = action ? action() : 0;
282 if ( IsRecording() )
283 Output( std::to_wstring( result ) );
284 return result;
285 }
286}
287
289{
290 // Unconsumed commands remaining in the input file is also an error condition.
291 if( !GetError() && !PeekTokens().empty() ) {
292 NextIn();
293 SetError();
294 }
295 if ( GetError() ) {
296 // Return nonzero
297 // Returning the (1-based) line number at which the script failed is a
298 // simple way to communicate that information to the test driver script.
299 return sLineNumber ? sLineNumber : -1;
300 }
301
302 // Return zero to mean all is well, the convention for command-line tools
303 return 0;
304}
305
306}
#define str(a)
const TranslatableString name
Definition: Distortion.cpp:82
The output stream of the journal system.
Journal system's error status, command dictionary, initializers.
FileConfig * gPrefs
Definition: Prefs.cpp:71
wxString FilePath
Definition: Project.h:20
static const wxChar * numbers[]
This specialization of Setting for bool adds a Toggle method to negate the saved value.
Definition: Prefs.h:286
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:143
Abstract base class used in importing a file.
~SyncException() override
Definition: Journal.cpp:110
void DelayedHandlerAction() override
Action to do in the main thread at idle time of the event loop.
Definition: Journal.cpp:112
bool Write(const T &value)
Write value to config and return true if successful.
Definition: Prefs.h:229
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:185
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
bool VersionCheck(const wxString &value)
True if value is an acceptable journal version number to be rerun.
Definition: Journal.cpp:85
Facilities for recording and playback of sequences of user interaction.
const Dictionary & GetDictionary()
bool OpenOut(const wxString &fullPath)
void Sync(const wxString &string)
Definition: Journal.cpp:230
bool RecordEnabled()
Definition: Journal.cpp:119
constexpr auto EscapeCharacter
Definition: JournalOutput.h:23
int IfNotPlaying(const wxString &string, const InteractiveAction &action)
Call action only if not replaying; synchronize on string and int values.
Definition: Journal.cpp:256
bool IsRecording()
bool SetRecordEnabled(bool value)
Definition: Journal.cpp:124
bool Dispatch()
Definition: Journal.cpp:202
constexpr auto SeparatorCharacter
Definition: JournalOutput.h:22
void Comment(const wxString &string)
bool GetError()
void Output(const wxString &string)
wxArrayStringEx GetTokens()
Definition: Journal.cpp:192
bool Begin(const FilePath &dataDir)
Definition: Journal.cpp:141
void SetError()
std::function< int() > InteractiveAction
Function that returns a value which will be written to the journal.
Definition: Journal.h:69
const Initializers & GetInitializers()
void SetInputFileName(const wxString &path)
Definition: Journal.cpp:136
constexpr auto CommentCharacter
Definition: JournalOutput.h:24
bool IsReplaying()
Definition: Journal.cpp:131
int GetExitCode()
Definition: Journal.cpp:288
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for, if Traits<Type>::iterated_type is defined.
Definition: PackedArray.h:126
auto begin(const Ptr< Type, BaseDeleter > &p)
Enables range-for, if Traits<Type>::iterated_type is defined.
Definition: PackedArray.h:112