Rewinding input data of TJSONIterator class with TRewindReaderProc - c++

I'm trying to parse JSON in a program built with embarcadero's c++builder (Tokyo 10.2 Update 3), which is not easy considering their severe lack of documentation.
I am using the TJSONIterator Find method that returns true or false if the Path (eg [0]['key'] or car.model['colour']) you give exists in the JSON data, which according to embarcadero's documentation needs a rewinding procedure passed to the constructor of the TJSONIterator class and if it's not there then an exception is thrown stating that.
The rewinding procedure should inherit the _di_TRewindReaderProc interface so here is my class.
class rewindclass : public TJSONIterator::_di_TRewindReaderProc
{
public:
void __fastcall Invoke(System::Json::Readers::TJsonReader* AReader)
{
//code to rewind Iterator
Areader->Rewind();
}
};
I'm not sure what should go into the Invoke function because as I said the documentation is useless. Obviously you have to do something with the TJsonReader that's passed and the only function I can see that could be used is Rewind but I don't think that's it because the only thing the documentation says about the TRewindReaderProc is
Reference to a procedure that rewinds the input data of the specified JSON reader.
Note: TJsonReader.Rewind does not rewind the input data, it resets the state of the JSON
reader. This procedure must rewind the actual data stream that provides the input data
of the JSON reader.
and I cannot see what else could be used instead. It says the actual data stream that provides the input must be reset but I'm not sure how to do this.
I'm using a TStringReader to read in the JSON data which is fed into a TJsonTextReader class constructor and that's fed into a TJSONIterator class constructor with a class that is using the _di_TRewindReaderProc interface.
//create rewindclass
rewindclass *rewind = new rewindclass();
//setting up TJSONIterator class
TStringReader *sread = new TStringReader(this->Memo1->Text);
TJsonTextReader *jread = new TJsonTextReader(sread);
TJSONIterator *jit = new TJSONIterator(jread, *rewind);
This code compiles ok but when I debug it and step into the TJSONIterator constructor the TJsonTextReader is not passed through and because of that when I call the Find method a second time it throws an exception saying no callback procedure set.
So does anyone know why the _di_TRewindReaderProc is not being passed through and what should go into the Invoke method?

I struggled for hours with the same problem in Delphi and finally I managed to find a way to make it works. I was using a TFileStream and TStreamReader as underlying readers instead of a TStringReader but I hope you understand the concepts behind any reader. This is my code:
procedure LoadFromFile(AFileName: string);
var
FS: TFileStream;
StreamReader: TStreamReader;
JTReader: TJsonTextReader;
JIterator: TJSONIterator;
RewindProc: TJSONIterator.TRewindReaderProc;
begin
RewindProc := procedure (AReader: TJsonReader)
begin
TStreamReader(TJsonTextReader(AReader).Reader).DiscardBufferedData;
TStreamReader(TJsonTextReader(AReader).Reader).BaseStream.Seek(0, TSeekOrigin.soBeginning);
end;
FS:=TFileStream.Create(AFileName, fmOpenRead);
StreamReader:=TStreamReader.Create(FS);
JTReader:=TJsonTextReader.Create(StreamReader);
JIterator:=TJSONIterator.Create(JTReader , RewindProc);
JIterator.Find('some.path.here');
JIterator.Find('other.path.here');
end;
The lines inside the RewindProc procedure make the magic. As documentation said, you must make the rewind of the underlying readers and that is what I did with these two lines. The first one clean the internal buffer of the StreamReader and the second one move the file pointer to the beginning.
I hope this help you in some way because the documentation is really poor and I can't find any article on Internet about how to set up properly the rewind procedure to be passed to the JSON iterator.
PD: Then I found some other troubles working with TIterator.Find but that is other matter so I focused on your specific question.

Since TJSONIterator alreadys calls Rewind on the reader, before it calls your Rewind procedure, calling Rewind again is not necessary. Instead reset the stream and discard all buffers of the Reader:
procedure TForm1.Button1Click(Sender: TObject);
const
JsonRec = '{"some":{"path":{"there":"ahi", "here":"acqui"}}}';
var
StringStream: TStringStream;
StreamReader: TStreamReader;
JsonTextReader: TJsonTextReader;
Iterator: TJSONIterator;
begin
JsonTextReader:= nil;
Iterator:= nil;
StringStream:= TStringStream.Create(JsonRec);
try
StreamReader:= TStreamReader.Create(StringStream);
JsonTextReader:= TJsonTextReader.Create(StreamReader);
Iterator:= TJSONIterator.Create(JsonTextReader,
procedure (AReader: TJSONReader)
var
v: TValue;
begin
StringStream.Seek(0, soBeginning);
StreamReader.DiscardBufferedData;
//workaround for RSP-24517
v:= TRttiContext.Create.GetType(TJsonTextReader).GetField('FChars').GetValue(AReader);
v.SetArrayElement(0, #0);
end);
if Iterator.Find('some.path.here') then
ecDebug.Lines.Add(Iterator.AsString);
if Iterator.Find('some.path.there') then
ecDebug.Lines.Add(Iterator.AsString);
finally
Iterator.Free;
JsonTextReader.Free;
StreamReader.Free;
StringStream.Free;
end;
end;
There doesn't seem to be a way to reset a TStringReader however, that's why I'm using a TStringStream instead.
Update: I've added the necessary call to DiscardBufferedData in the rewind procedure. This only showed up after tests with bigger files.
Update2: With json files larger than 1K, there's a workaround to a bug in TJsonTextReader required, which fails to clear FChars, so it isn't re-reading the json file after a call to .Rewind which causes an exception "Unexpected character encountered while parsing value...". To access the private FChars, I'm using RTTI as described in https://stackoverflow.com/a/36717896/386473. The bug is logged in QP as https://quality.embarcadero.com/browse/RSP-24517.

Related

uwp: How download files when app is in suspended mode

There is queue with links of files to download. I'm trying find the way to continue downloading when application goes to suspend mode.
According to official microsoft documentation suitable class for this is BackgroundDownloader, but it's handles only one current downloading process. It looks wrong to call in loop CreateDownload() method for every link without waiting for the completion of previous links, isn't right?
More logical in my opinion is using in-process background task. I see it this way:
Implement Run(IBackgroundTaskInstance) method of interface IBackgroundTask (it should stay alive even when app is suspended, right?)
Using custom event transmit the queue to the implemented method
Inside Run(IBackgroundTaskInstance) method use BackgroundDownloader (by implementing the execution of one instance at a time)
But I'm stuck even with simple implementation for one file downloading. Bellow my Run(IBackgroundTaskInstance) method implementation:
void Task::DownloaderTask::Run(IBackgroundTaskInstance ^ taskInstance)
{
TaskDeferral = taskInstance->GetDeferral();
std::wstring filename = L"Pleiades_large.jpg";
Uri^ uri = ref new Uri(ref new Platform::String(L"https://upload.wikimedia.org/wikipedia/commons/4/4e/Pleiades_large.jpg"));
Concurrency::create_task(KnownFolders::GetFolderForUserAsync(nullptr, KnownFolderId::PicturesLibrary))
.then([this, filename, uri](StorageFolder^ picturesLibrary)
{
return picturesLibrary->CreateFileAsync(ref new Platform::String(filename.c_str()), CreationCollisionOption::GenerateUniqueName);
}).then([this, filename, uri](StorageFile^ destinationFile) {
BackgroundDownloader^ downloader = ref new BackgroundDownloader();
DownloadOperation^ download = downloader->CreateDownload(uri, destinationFile);
download->StartAsync();
}).then([this](Concurrency::task<void> previousTask)
{
try
{
previousTask.get();
TaskDeferral->Complete();
}
catch (Platform::Exception^ ex)
{
wchar_t buffer[1024];
swprintf_s(buffer, L"Exception: %s", ex->Message);
OutputDebugString(buffer);
}
});
}
The code above only creates empty file, but using the same code without BackgroundTask it works correctly. I didn't find any restrictions for BackgroundDownloader inside BackgroundTask.
So, my questions are:
Is it right way of usage BackgroundTask?
Is there another approach to solving the problem?
Is this problem solvable at all?
I've found the cause of the unexpected behavior:
The line of code TaskDeferral->Complete(); was at the end of the method at first while it should be at the end of async call.
Therefore, initial implementation (published in question) is correct.
All that had to be done was to Rebuild project.

How to reset header buffer in Lucee?

I am trying to write a unit test for my ColdBox application running on Lucee 4.5 using testbox for a function that includes a cfhtmlhead() call.
Unfortunately, the string that would normally be appended to the <head> section of the HTML output using that function is appended to the output of the unit test instead, causing the test to fail.
The output of cfhtmlhead() is obviously written to a special buffer. According to a blog post it is possible to clear that buffer. The example function shown there looks like this:
function clearHeaderBuffer() {
local.out = getPageContext().getOut();
while (getMetaData(local.out).getName() is "coldfusion.runtime.NeoBodyContent") {
local.out = local.out.getEnclosingWriter();
}
local.method = local.out.getClass().getDeclaredMethod("initHeaderBuffer", arrayNew(1));
local.method.setAccessible(true);
local.method.invoke(local.out, arrayNew(1));
}
Though the blog post is written for Adobe ColdFusion and it obviously doesn't work the same way in Lucee.
By dumping local.out I saw that the object has a method resetHTMLHead(). But calling that method doesn't seem to work, either (even when the related getHTMLHead() method outputs the string from the cfhtmlhead() call).
So, how to reset the header buffer in Lucee?
I found the answer by checking the Lucee sources. There the buffer is accessed via getRootOut().getHTMLHead().
So the code to clear the header buffer boils down to this:
function clearHeaderBuffer() {
getPageContext().getRootOut().resetHTMLHead();
}

How to unregister style using TStyleManager::UnRegisterStyle()

I want to unregister a specific style using this code:
void __fastcall TfrmMain::btnUnregStyleClick(TObject *Sender)
{
TCustomStyleServices *MyStyle;
// Get wanted style
MyStyle = TStyleManager::Style["Emerald"]; // this will return a TCustomStyleServices obj
if (MyStyle != NULL)
{
// Remove it
TStyleManager::UnRegisterStyle(MyStyle); // This will set default Windows style (no style)
}
}
It works. The style seems to be unregistered, and the GUI switches automatically to the default Windows style.
But when program shuts down, I get this error:
Project Project.exe raised exception class $C0000005 with message
'access violation at 0x5005fd50: read of address 0xffffffd0'.
Here is the Call stack:
:5005fd50 rtl250.#System#TObject#InheritsFrom$qqrp17System#TMetaClass + 0x8
:50d12a8d vcl250.#Vcl#Styles#TStyleEngine#Notification$qqr54Vcl#Themes#TCustomStyleEngine#TStyleEngineNotificationpv + 0x1d
:00e5a612 vclwinx250.#Vcl#Winxctrls#TSearchBox#$bcdtr$qqrv + 0x1e
:0041fa0f __cleanup + 0x1F
:0041fb92 ; __wstartup
[Update: this crash is fixed if I remove TeeChart from my form. But UnRegisterStyle() still won't work]
If after UnRegisterStyle() I call:
TStyleManager::LoadFromFile(usStylePath);
TStyleManager::SetStyle("Emerald");
it will tell me that the "emerald style is already registered".
So, obviously UnRegisterStyle() fails.
Getting the "styles" list via TStyleManager::StyleNames() shows that the list remains unchanged after UnRegisterStyle().
Embarcadero has no help on this function. Should I call something else, additionally to UnRegisterStyle()?
Answer has been updated! Again.
Well, I am not very experienced user of VCL's styles but I will try to explain what to do to avoid your problem. Before you will read further I should tell you: there is no way to unregister any style.
Style or not Style
First, you must know that VCL's styles uses internal FRegisteredStyles dictionary to store all registered styles. Style became registered after TStyleManager.LoadFromFile(YourStyleName) has been called. Unfortunately, style never gets removed from dictionary.
Do not use UnregisterStyle procedure. It doesn't unregister style at all. Simply removes specified style from the list of available styles. Thus, after you call UnregisterStyle(YourStyle) you just wipe out YourStyle from internal list, not from dictionary mentioned earlier and cannot set this style.
After successful call to UnregisterStyle() you may call LoadFromFile() method and wonder why applicaition said:
Style 'Emerald' already registered.
This happens because LoadFromFile() method loads specified style and checks its existing in internal dictionary of registered styles. This checking always returns true as UnregisterStyle() procedure doesn't delete specified style from dictionary.
The same thing related also with property StyleNames. This property returns name of style uses internal FRegisteredStyles dictionary to obtain name of style with specified index.
If you want to know my opinion, such behaviour of style is weird. Method UnregisterStyle() should also delete specified style from dictionary. Maybe I am wrong but this is a real bug. On the other hand, there is no bug - you are trying to use undocumented method. The truth is somewhere near.
Styled Result
As conclusion, I would recommend you to use direct access to styles to decide can you use the selected style or not. See the code below (assuming you have styling of application turned on):
procedure TForm1.Button1Click(Sender: TObject);
begin
if Assigned(TStyleManager.Style['Emerald']) and TStyleManager.Style['Emerald'].Available then
// Do your code
else
ShowMessage('Specified style is not available!');
end;
If you have sources of VCL.Themes you can easily check the plausibility of what I wrote here.
High Technology
I have created class-helper for TStyleManager that allows to check if style-file registered and unregister it if needed. In order to fill TStyleInfo record with real values from style-file, we need save specified style to TMemoryStream and then pass stream to IsValidStyle function. Also we could use physical path to style (f.e. C:\Embarcadero\Delphi\Styles\Emerald.vsf) instead of variant with stream, but latter looks more elegant.
Unfortunately, according to Remy's comment (I don't know how to make a link to it), C++ Builder (which is used by OP) doesn't support class-helper feature. The only solution is to create simple unit which contains all code needed for class-helper. To work with it is enough to add such unit to uses clause and call appropriate public procedures\functions.
This unit have the following structure:
unit StyleManager_CH;
interface
uses
VCL.Themes;
function IsStyleRegistered_CH(var AStyle: TCustomStyleServices): Boolean;
procedure UnregisterStyleEx_CH(var AStyle: TCustomStyleServices);
implementation
uses
System.SysUtils, System.Classes, System.Generics.Collections;
type
TStyleManagerHelper = class helper for TStyleManager
public
class function IsStyleRegistered(var AStyle: TCustomStyleServices): Boolean;
class procedure UnregisterStyleEx(var AStyle: TCustomStyleServices);
end;
class function TStyleManagerHelper.IsStyleRegistered(var
AStyle: TCustomStyleServices): Boolean;
begin
Result := Assigned(AStyle) and
TStyleManager.FRegisteredStyles.ContainsKey(AStyle.Name);
end;
class procedure TStyleManagerHelper.UnregisterStyleEx(var
AStyle: TCustomStyleServices);
var
MS: TMemoryStream;
StyleInfo: TStyleInfo;
SourceInfo: VCL.Themes.TStyleManager.TSourceInfo;
begin
if Assigned(AStyle) then
begin
MS := TMemoryStream.Create;
try
AStyle.SaveToStream(MS);
MS.Position := 0;
if AStyle.IsValidStyle(MS, StyleInfo) then
begin
if TStyleManager.FRegisteredStyles.ContainsKey(StyleInfo.Name) then
begin
SourceInfo := TStyleManager.FRegisteredStyles.Items[StyleInfo.Name];
if Assigned(SourceInfo.Data) then
FreeAndNil(SourceInfo.Data);
TStyleManager.FStyles.Remove(AStyle);
TStyleManager.FRegisteredStyles.Remove(StyleInfo.Name);
FreeAndNil(AStyle);
end;
end;
finally
MS.Free;
end;
end;
end;
function IsStyleRegistered_CH(var AStyle: TCustomStyleServices): Boolean;
begin
Result := TStyleManager.IsStyleRegistered(AStyle);
end;
procedure UnregisterStyleEx_CH(var AStyle: TCustomStyleServices);
begin
TStyleManager.UnregisterStyleEx(AStyle);
end;
end.
Addendum _CH stands for class-helper abbreviation. There were also fixed some memory leaks.
I am not sure if there is another way to reload style-file "on-the-fly".

How to test that a file is left unchanged?

I'm testing a function that may modify a file. How do i test that it is unchanged in the cases where I want it to?
I don't want to check the content, because the file may have been overwritten with the same content, changing the modification time.
I can't really check the modification time, either. Since I like tests to be self-contained, the original file would be written just before the (non-)modification test, rendering the modification time unreliable.
You can use DI to mock your filewriter. This way you do not need the file at all, only check if the write function is called and you know if the file was modified.
I would split the function into two separate functions; the first decides whether the modification should be made, the second makes the notification. The second is only called if necessary. In pretend language:
function bool IsModificationRequired()
{
// return true or false based on your actual code
}
function void WriteFile()
{
new File().Write("file");
}
function void WriteIfModified()
{
if (IsModificationRequired())
WriteFile();
}
And test
Assert.IsTrue(IsModificationRequired());
Well assuming you are using a text file and reasonable size. Just hash the file content, if before modify and after modidfy hashcode is same then - it means the file content is not changed.
Here is the link to Algorithim Design Manual - Steve Skiena (Google Book Result)
Section 3.8
How can i convicne you that a file isn't changed ?

Unexpected/weird results using OLE clipboard and classic clipboard, or do I miss something?

I'm trying to do the following trick:
I have IDataObject* to be set into the clipboard, so I'm using OleSetClipboard() to set it into the clipboard.
I have another CLIPFORMAT I want to add to the clipboard, but I can't do it with OleSetClipboard() because the IDataObject* I receive does not implement SetData() method. So, to overcome this limitation I OpenClipboard() with GetClipboardOwner(), this way, I can SetClipboardData() to the clipboard without EmptyClipboard() first.
Now, it all works well, but what happens is that OleGetClipboard() does not return the data I placed in the clipboard using SetClipboardData(), but I can using GetClipboardData().
I can imagine why this happens (It just returns the IDataObject*), so I tried to OleFlushClipboard() to delete the IDataObject*, and OleGetClipboard() again to let the OS rebuild a new IDataObject*, and it still didn't contain the CLIPFORMAT added by SetClipboardData().
Does anyone have any idea how to overcome this issue? or a different trick? or even explain why it works this way? Thanks
I just tried this (on Windows 7) and it appears to work but only cross-process:
In a different process to the clipboard owner, OleGetClipboard returns a data object that contains all of the formats (i.e. the original formats from the data object and the extra ones added to the clipboard).
In the same process, OleGetClipboard always returns a data object that does not contain the extra clipboard formats.
In both cases, calling OleFlushClipboard makes no difference.
Anyway, this doesn't seem like a terribly robust solution. What you can do instead is create your own data object that responds to the formats it knows about and delegates other formats to the original data object. The EnumFormatEtc method would combine formats from both objects, and so on. This article has the skeleton of a simple data object you could extend.