Audacity  3.0.3
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 
27 namespace Journal {
28 
29 namespace {
30 
31 wxString sFileNameIn;
32 wxTextFile sFileIn;
33 
34 wxString sLine;
35 // Invariant: the input file has not been opened, or else sLineNumber counts
36 // the number of lines consumed by the tokenizer
37 int sLineNumber = -1;
38 
39 BoolSetting JournalEnabled{ L"/Journal/Enabled", false };
40 
41 inline void NextIn()
42 {
43  if ( !sFileIn.Eof() ) {
44  sLine = sFileIn.GetNextLine();
45  ++sLineNumber;
46  }
47 }
48 
50 {
51  wxArrayStringEx tokens;
52  if ( Journal::IsReplaying() )
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 
67 constexpr auto VersionToken = wxT("Version");
68 
69 // Numbers identifying the journal format version
71  1
72 };
73 
74 wxString 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 
85 bool 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(
98  std::begin( journalVersionNumbers ), std::end( journalVersionNumbers ),
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 
124 bool 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 
136 void SetInputFileName(const wxString &path)
137 {
138  sFileNameIn = path;
139 }
140 
141 bool 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 
202 bool Dispatch()
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 
230 void 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 
243 void Sync( const wxArrayString &strings )
244 {
245  if ( IsRecording() || IsReplaying() ) {
246  auto string = ::wxJoin( strings, SeparatorCharacter, EscapeCharacter );
247  Sync( string );
248  }
249 }
250 
251 void 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 }
Journal::SetInputFileName
void SetInputFileName(const wxString &path)
Definition: Journal.cpp:136
Journal::anonymous_namespace{Journal.cpp}::JournalEnabled
BoolSetting JournalEnabled
Definition: Journal.cpp:39
Journal::GetDictionary
const Dictionary & GetDictionary()
Definition: JournalRegistry.cpp:53
Journal::anonymous_namespace{Journal.cpp}::VersionToken
constexpr auto VersionToken
Definition: Journal.cpp:67
Journal
Facilities for recording and playback of sequences of user interaction.
Journal::OpenOut
bool OpenOut(const wxString &fullPath)
Definition: JournalOutput.cpp:33
gPrefs
FileConfig * gPrefs
Definition: Prefs.cpp:70
str
#define str(a)
Definition: DBConnection.cpp:30
Journal::CommentCharacter
constexpr auto CommentCharacter
Definition: JournalOutput.h:24
Setting::Write
bool Write(const T &value)
Write value to config and return true if successful.
Definition: Prefs.h:172
Format
Abstract base class used in importing a file.
Journal::Dispatch
bool Dispatch()
Definition: Journal.cpp:202
BoolSetting
This specialization of Setting for bool adds a Toggle method to negate the saved value.
Definition: Prefs.h:204
Journal::anonymous_namespace{Journal.cpp}::NextIn
void NextIn()
Definition: Journal.cpp:41
Journal::SeparatorCharacter
constexpr auto SeparatorCharacter
Definition: JournalOutput.h:22
wxArrayStringEx
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
Definition: wxArrayStringEx.h:18
numbers
static const wxChar * numbers[]
Definition: SelectionBar.cpp:73
Journal::anonymous_namespace{Journal.cpp}::sLine
wxString sLine
Definition: Journal.cpp:34
Journal::Begin
bool Begin(const FilePath &dataDir)
Definition: Journal.cpp:141
Setting::Read
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:128
Journal::GetExitCode
int GetExitCode()
Definition: Journal.cpp:288
Journal::anonymous_namespace{Journal.cpp}::sFileNameIn
wxString sFileNameIn
Definition: Journal.cpp:31
Journal::anonymous_namespace{Journal.cpp}::VersionString
wxString VersionString()
Definition: Journal.cpp:74
Journal.h
Journal::SyncException::SyncException
SyncException()
Definition: Journal.cpp:104
Journal::Output
void Output(const wxString &string)
Definition: JournalOutput.cpp:45
Journal::GetError
bool GetError()
Definition: JournalRegistry.cpp:30
FilePath
wxString FilePath
Definition: Project.h:20
Journal::anonymous_namespace{Journal.cpp}::sFileIn
wxTextFile sFileIn
Definition: Journal.cpp:32
Journal::IsRecording
bool IsRecording()
Definition: JournalOutput.cpp:28
Journal::anonymous_namespace{Journal.cpp}::PeekTokens
wxArrayStringEx PeekTokens()
Definition: Journal.cpp:49
Journal::SetError
void SetError()
Definition: JournalRegistry.cpp:35
name
const TranslatableString name
Definition: Distortion.cpp:98
Journal::Comment
void Comment(const wxString &string)
Definition: JournalOutput.cpp:62
Journal::GetTokens
wxArrayStringEx GetTokens()
Definition: Journal.cpp:192
Journal::RecordEnabled
bool RecordEnabled()
Definition: Journal.cpp:119
Journal::SyncException
Definition: Journal.h:87
Journal::IsReplaying
bool IsReplaying()
Definition: Journal.cpp:131
JournalRegistry.h
Journal system's error status, command dictionary, initializers.
FileConfig::Flush
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:143
Journal::EscapeCharacter
constexpr auto EscapeCharacter
Definition: JournalOutput.h:23
MemoryX.h
Journal::SetRecordEnabled
bool SetRecordEnabled(bool value)
Definition: Journal.cpp:124
Journal::InteractiveAction
std::function< int() > InteractiveAction
Function that returns a value which will be written to the journal.
Definition: Journal.h:69
Journal::anonymous_namespace{Journal.cpp}::VersionCheck
bool VersionCheck(const wxString &value)
True if value is an acceptable journal version number to be rerun.
Definition: Journal.cpp:85
JournalOutput.h
The output stream of the journal system.
Journal::anonymous_namespace{Journal.cpp}::journalVersionNumbers
int journalVersionNumbers[]
Definition: Journal.cpp:70
Journal::Events::anonymous_namespace{JournalEvents.cpp}::initializer
RegisteredInitializer initializer
Definition: JournalEvents.cpp:449
Journal::IfNotPlaying
int IfNotPlaying(const wxString &string, const InteractiveAction &action)
Call action only if not replaying; synchronize on string and int values.
Definition: Journal.cpp:256
Prefs.h
Journal::SyncException::~SyncException
~SyncException() override
Definition: Journal.cpp:110
Journal::GetInitializers
const Initializers & GetInitializers()
Definition: JournalRegistry.cpp:69
Journal::SyncException::DelayedHandlerAction
void DelayedHandlerAction() override
Action to do in the main thread at idle time of the event loop.
Definition: Journal.cpp:112
Journal::Sync
void Sync(const wxString &string)
Definition: Journal.cpp:230
Journal::anonymous_namespace{Journal.cpp}::sLineNumber
int sLineNumber
Definition: Journal.cpp:37