I am using C++ and I read in and parse an XML file. If the file does not exist it gets created and if it does exist and has correctly formatted data in it there no errors. My problem is when I try to read in a blank XML file (completely blank not even the header) my program crashes as it tries to process the data from the XML file. I need to be able to catch the error before that happen so I can wright the correct settings back to the file or remove the file and make a new one. Is there a way to do this such as checking to see if a node is NULL? And how would I do it?
class XmlDomErrorHandler : public HandlerBase
{
public:
void fatalError(const SAXParseException &exc) {
printf("Fatal parsing error at line %d\n", (int)exc.getLineNumber());
exit(-1);
}
};
void XmlParentNode::refreshAllNodes()
{
m_children.clear();
m_childrernByName.clear();
for(int j =0; j < getInnerParentNode()->getChildNodes()->getLength(); ++j) //crashes here on first pass if xmlfile is empty
{
//code that is never reached
}
XmlDocument::XmlDocument(String name):
XmlParentNode(),
m_pOwner(NULL)
{
if(XmlManager::Get()->initialize())
{
XMLCh* xmlName = XMLString::transcode(StringA(name));
setInnerNode(XmlManager::Get()->getDOMImplementation()->createDocument(NULL, xmlName , NULL));
XMLString::release(&xmlName);
}
}
XmlDocument::XmlDocument(FilePath path):
XmlParentNode(),
m_pOwner(NULL)
{
XMLCh* xmlPath = XMLString::transcode(StringA(path.ToString()));
XercesDOMParser* parser = new XercesDOMParser();
parser->parse(xmlPath);
if(!parser)
{
ErrorHandler* errorHandler = (ErrorHandler*) new XmlDomErrorHandler();
parser->setErrorHandler(errorHandler);
}
DOMDocument* p_Doc= parser->getDocument();
m_pOwner = parser;
XMLString::release(&xmlPath);
if (p_Doc!= NULL)
{
setInnerNode(p_Doc);
refreshAllNodes();
}
}
Sadly I can't stop people for editing the XML file as users need access to it so they can edit settings.
I am new to XML so maybe I'm missing something easy.
I have been using XML Parsing with DOM and a guide I found here
I solved the problem.
I added a check before the for loop that crashed.
if(getInnerParentNode() && getInnerParentNode()->getChildNodes())
{
for(int j =0; j < getInnerParentNode()->getChildNodes()->getLength(); ++j) //was crashing here on first pass if xmlfile is empty
{
//code that was not being reached
}
}
Related
I want to know how to figure out file path is from internal or external storage.
I want to delete a file. Before deleting it i want to check whether it is from internal memory or external.
if file is from internal storage then i can simply delete it like this
file.delete();
But if file is from external storage (sdcard). Then i would first check permission then delete it through storage access framework.
I'm currently doing like this.
File selectedFile = Constant.allMemoryVideoList.get(fPosition).getFile().getAbsoluteFile();
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (???????????? check if file is not from internal storage ???????????) {
List<UriPermission> permissions = getContentResolver().getPersistedUriPermissions();
if (permissions != null && permissions.size() > 0) {
sdCardUri = permissions.get(0).getUri();
deleteFileWithSAF();
} else {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Please select external storage directory (e.g SDCard)")
.setMessage("Due to change in android security policy it is not possible to delete or rename file in external storage without granting permission")
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// call document tree dialog
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, REQUEST_CODE_OPEN_DOCUMENT_TREE);
}
})
.setNegativeButton("CANCEL", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
})
.show();
}
} else {
deleteFile();
}
} else {
deleteFile();
}
deleteFileWithSAF()
private void deleteFileWithSAF() {
//First we get `DocumentFile` from the `TreeUri` which in our case is `sdCardUri`.
DocumentFile documentFile = DocumentFile.fromTreeUri(this, sdCardUri);
//Then we split file path into array of strings.
//ex: parts:{"", "storage", "extSdCard", "MyFolder", "MyFolder", "myImage.jpg"}
// There is a reason for having two similar names "MyFolder" in
//my exmple file path to show you similarity in names in a path will not
//distract our hiarchy search that is provided below.
String[] parts = (selectedFile.getPath()).split("\\/");
// findFile method will search documentFile for the first file
// with the expected `DisplayName`
// We skip first three items because we are already on it.(sdCardUri = /storage/extSdCard)
for (int i = 3; i < parts.length; i++) {
if (documentFile != null) {
documentFile = documentFile.findFile(parts[i]);
}
}
if (documentFile == null) {
// File not found on tree search
// User selected a wrong directory as the sd-card
// Here must inform user about how to get the correct sd-card
// and invoke file chooser dialog again.
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Please select root of external storage directory (click SELECT button at bottom)")
.setMessage("Due to change in android security policy it is not possible to delete or rename file in external storage without granting permission")
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// call for document tree dialog
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, REQUEST_CODE_OPEN_DOCUMENT_TREE);
}
})
.setNegativeButton("CANCEL", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
})
.show();
} else {
// File found on sd-card and it is a correct sd-card directory
// save this path as a root for sd-card on your database(SQLite, XML, txt,...)
// Now do whatever you like to do with documentFile.
// Here I do deletion to provide an example.
if (documentFile.delete()) {// if delete file succeed
// Remove information related to your media from ContentResolver,
// which documentFile.delete() didn't do the trick for me.
// Must do it otherwise you will end up with showing an empty
// ImageView if you are getting your URLs from MediaStore.
getApplicationContext().sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(selectedFile)));
// Methods.removeMedia(this,selectedFile.getPath());
if (deleteSingleFileCall){
Constant.allMemoryVideoList.remove(videoPosition);
adapter.notifyItemRemoved(videoPosition);
deleteSingleFileCall = false;
}
/*update the playback record to
* getFileName() contain file.getName()*/
for (int i = 0; i < Constant.filesPlaybackHistory.size(); i++) {
if ((selectedFile.getName()).equals(Constant.filesPlaybackHistory.get(i).getFileName())) {
Constant.filesPlaybackHistory.remove(i);
break;
}
}
//save the playback history
Paper.book().write("playbackHistory", Constant.filesPlaybackHistory);
}
}
}
This is how i load files of both internal and external storage.
StorageUtil is library https://github.com/hendrawd/StorageUtil
String[] allPath = StorageUtil.getStorageDirectories(this);
private File directory;
for (String path: allPath){
directory = new File(path);
Methods.load_Directory_Files(directory);
}
All Loaded files in following arraylist.
//all the directory that contains files
public static ArrayList<File> directoryList = null;
//list of all files (internal and external)
public static ArrayList<FilesInfo> allMemoryVideoList = new ArrayList<>();
FilesInfo: Contain all info about file like thumbnail, duration, directory, new or played before, if played then last playback position etc
LoadDirectoryFiles()
public static void load_Directory_Files(File directory) {
//Get all file in storage
File[] fileList = directory.listFiles();
//check storage is empty or not
if(fileList != null && fileList.length > 0)
{
for (int i=0; i<fileList.length; i++)
{
boolean restricted_directory = false;
//check file is directory or other file
if(fileList[i].isDirectory())
{
for (String path : Constant.removePath){
if (path.equals(fileList[i].getPath())) {
restricted_directory = true;
break;
}
}
if (!restricted_directory)
load_Directory_Files(fileList[i]);
}
else
{
String name = fileList[i].getName().toLowerCase();
for (String ext : Constant.videoExtensions){
//Check the type of file
if(name.endsWith(ext))
{
//first getVideoDuration
String videoDuration = Methods.getVideoDuration(fileList[i]);
long playbackPosition;
long percentage = C.TIME_UNSET;
FilesInfo.fileState state;
/*First check video already played or not. If not then state is NEW
* else load playback position and calculate percentage of it and assign it*/
//check it if already exist or not if yes then start from there else start from start position
int existIndex = -1;
for (int j = 0; j < Constant.filesPlaybackHistory.size(); j++) {
String fListName = fileList[i].getName();
String fPlaybackHisName = Constant.filesPlaybackHistory.get(j).getFileName();
if (fListName.equals(fPlaybackHisName)) {
existIndex = j;
break;
}
}
try {
if (existIndex != -1) {
//if true that means file is not new
state = FilesInfo.fileState.NOT_NEW;
//set playbackPercentage not playbackPosition
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(fileList[i].getPath());
String time = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
retriever.release();
int duration = Integer.parseInt(time);
playbackPosition = Constant.filesPlaybackHistory.get(existIndex).getPlaybackPosition();
if (duration > 0)
percentage = 1000L * playbackPosition / duration;
else
percentage = C.TIME_UNSET;
}
else
state = FilesInfo.fileState.NEW;
//playbackPosition have value in percentage
Constant.allMemoryVideoList.add(new FilesInfo(fileList[i],
directory,videoDuration, state, percentage, storageType));
//directory portion
currentDirectory = directory.getPath();
unique_directory = true;
for(int j=0; j<directoryList.size(); j++)
{
if((directoryList.get(j).toString()).equals(currentDirectory)){
unique_directory = false;
}
}
if(unique_directory){
directoryList.add(directory);
}
//When we found extension from videoExtension array we will break it.
break;
}catch (Exception e){
e.printStackTrace();
Constant.allMemoryVideoList.add(new FilesInfo(fileList[i],
directory,videoDuration, FilesInfo.fileState.NOT_NEW, C.TIME_UNSET, storageType));
}
}
}
}
}
}
Constant.directoryList = directoryList;
}
Image So reader could easily understand what is going on.
I'm currently working on a project with MFC and I noticed something weird that apparently has been there for a couple of years. When I launch the .exe of the program, it will do n number of things including reading a .DAT file and storing it as well. If the file doesn't exists, the program will try to find it with no luck throwing this CFile exception: The file could not be located. Which is correct because it doesn't exists. I have to do some operations first to generate that file, the storing process works fine. When the file exists and I run the program again, it's supposed read the file but this CArchive exception shows up: Invalid file format. And I don't understand why.
This is the Serialize():
//Fruits.cpp
void CFruits::Serialize(CArchive &ar)
{
int nVersion = 0;
CObject::Serialize(ar);
ar.SerializeClass(GetRuntimeClass());
if(ar.IsStoring())
{
ar.Write(&m_bInit,sizeof(bool));
ar.Write(&m_bYummy,sizeof(bool));
ar.Write(&m_bAcid, sizeof(bool));
ar.Write(&m_bFresh,sizeof(bool));
...
...
...
ar<<m_cType;
ar<<m_cColour;
ar<<m_cFlavor;
ar<<m_cPrice;
ar<<m_cQuantity;
}
else
{
nVersion = ar.GetObjectSchema();
ar.Read(&m_bInit,sizeof(bool));
ar.Read(&m_bYummy,sizeof(bool));
ar.Read(&m_bAcid, sizeof(bool));
ar.Read(&m_bFresh,sizeof(bool));
...
...
...
if( nVersion >= 2 || nVersion < 0)
ar<<m_cType;
else
m_cType=0;
if (nVersion >= 3 || nVersion < 0)
ar<<m_cColour;
else
m_cColour=0;
if (nVersion >= 4 || nVersion < 0)
ar<<m_cFlavor;
else
ar<<m_cFlavor=0;
if( nVersion >= 5 || nVersion < 0)
{
ar<<m_cPrice;
ar<<m_cQuantity;
}
else
{
m_cPrice=0;
m_cQuantity=0;
}
}
m_oSales.Serialize(ar);
m_oAdmin.Serialize(ar);
...
...
}
IMPLEMENT_SERIAL(CVehiculo,CObject,VERSIONABLE_SCHEMA | 6)
This is the SerializeElements:
//Fruits.cpp
void AFXAPI SerializeElements(CArchive &ar,CFruits * fruits,int ncount)
{
try
{
for(cont=0;cont<ncount;cont++)
fruits[cont].Serialize(ar);
}
catch(CArchiveException *AE)
{
//Here it stores the exception in a Log. Exception 5
}
}
The serializeElements is used to store and read the file n times, as declared here in the header file of fruits:
//Fruits.h
class CFruits : public CObject
{
public:
CFruits();
CFruits(const CFruits &O);
virtual ~CFruits();
void operator = (const CFruits &O);
void Serialize(CArchive &ar);
protected:
DECLARE_SERIAL(CFruits)
};
void AFXAPI SerializeElements(CArchive &ar,CFruits * fruits,int ncount);
typedef CArray<CFruits, CFruitso&> TArrayFruits;
The values of this Array, and the methods used to call the serialize are defined here in my main function:
//main.h
#include "CFruits.h"
class CMain : public CDialog
{
// Construction
public:
enum T_Fruits { eFruitsOnLine, eFruitsIng, eFruitsTra, eFruitsAnt, eFruitsP3, eFruitsP2, eFruitsP1, eFruitsC1, eFruitsC0, eFruitsEscape, eFruitsVideo};
private:
void StoreFruits();
void ReadFruits();
The SerializeElements for-loop is supposed to run 11 times, but I noticed that it only does it 1 time, then the Schema version changes to -1, (originally 6 cause I managed to trace the value). This happens only when reading the file.
I've tried the following:
I can't use debug so I have to use Logs, I placed a Log after every sentence in the Serialize() function, I found what seems to be the issue, this line:
ar.SerializeClass(GetRuntimeClass());
I used a try-catch and found that when that sentence happens, it throws the exception so, it doesn't continue reading. That is the moment where the version changes to -1. I tried to change that to:
ar.SerializeClass(RUNTIME_CLASS(CFruits));
Is the same result, I've read many forums trying to find the answer but I can't seem to do so. I've read the documentation and I found this here:
https://learn.microsoft.com/en-us/cpp/mfc/reference/carchive-class?view=vs-2019#serializeclass
Like ReadClass, SerializeClass verifies that the archived class
information is compatible with your runtime class. If it is not
compatible, SerializeClass will throw a CArchiveException.
But it doesn't make sense to me, because it doesn't fail storing. Should I look into something else?
Thank you
EDIT:
I'm posting the Store and Read methods
void CMain::ReadFruits()
{
CString CSerror, sFileName;
CString sDebug;
try
{
sFileName.Format("FRUITS%03d.DAT",GetNumT());
CFile fFruitsTag(sFileName,CFile::modeRead);
CArchive ar(&fFruitsTag,CArchive::load);
m_vFruits.Serialize(ar);
ar.Close();
fFruitsTag.Close();
}
catch(CFileException *FE)
{
...
}
catch(CArchiveException *AE)
{
...
}
}
void CMain::StoreFruits()
{
CString CSerror, sFileName;
try
{
if(!m_bStoringFruits)
{
sFileName.Format("FRUITS%03d.DAT",GetNumT());
m_bStoringFruits=true;
CFile fFruitsTag(sFileName,CFile::modeCreate|CFile::modeWrite);
CArchive ar(&fFruitsTag,CArchive::store);
m_vFruits.Serialize(ar);
ar.Close();
fFruitsTag.Close();
m_bStoringFruits=false;
}
}
catch(CFileException *FE)
{
...
}
catch(CArchiveException *AE)
{
...
}
catch(CException *e)
{
...
}
}
I am using ifstream to open a file and read line by line and print to console.
Now, I also want to make sure that if the file gets updated, it reflects. My code should handle that.
I tried setting fseek to end of the file and then looking for new entries by using peek. However, that did not work.
Here's some code I used
bool ifRead = true;
while (1)
{
if (ifRead)
{
if (!file2read.eof())
{
//valid file. not end of file.
while (getline(file2read, line))
printf("Line: %s \n", line.c_str());
}
else
{
file2read.seekg(0, file2read.end);
ifRead = false;
}
}
else
{
//I thought this would check if new content is added.
//in which case, "peek" will return a non-EOF value. else it will always be EOF.
if (file2read.peek() != EOF)
ifRead = true;
}
}
}
Any suggestions on what could be wrong or how I could do this.
i am currently trying to figure out a way to write a file (an allegro configuration file to be exact) to a mounted zip-file using physfs and allegro 5.
reading the config file works fine, but when it comes to writing the changed config, nothing happens (e.g. the file is not re-written and thus remains in it's old state).
also, when not using physfs, everything works perfectly.
here's the code i use:
Game::Game(int height, int width, int newDifficulty)
{
PHYSFS_init(NULL);
if (!PHYSFS_addToSearchPath("Data.zip", 1)) {
// error handling
}
al_set_physfs_file_interface();
cfg = al_load_config_file("cfg.cfg");
if (cfg != NULL) // file exists, read from it
{
const char *score = al_get_config_value(cfg, "", "highScore");
highScore = atoi(score); // copy value
}
else // file does not exist, create it and init highScore to 0
{
cfg = al_create_config();
al_set_config_value(cfg, "", "highScore", "0");
highScore = 0;
al_save_config_file("cfg.cfg", cfg);
}
...
}
and in another function:
void Game::resetGame()
{
// high score
if (player->getScore() > highScore)
{
highScore = player->getScore();
// convert new highScore to char* that can be saved
stringstream strs;
strs << highScore;
string temp_str = strs.str();
char const* pchar = temp_str.c_str();
if (cfg != NULL) // file exists, read from it
{
al_set_config_value(cfg, "", "highScore", pchar);
al_save_config_file("cfg.cfg", cfg);
}
}
...
}
since the code works without physfs, i guess i handle the config file itself correctly.
any help would be highly appreciated!
cheers,
hannes
in the meantime, i solved the issue myself.
apparently, physfs has no ability to write to an archive.
therefore, i need to PHYSFS_setWriteDir("jhdsaf"), save the cfg-file in that folder and then replace the original zip-file by an updated version with the cfg-file, just before the game closes (after all resources are unloaded because the zip is otherwise still in use).
if anyone is interested in the code to do this, just reply to this post!
hannes
Is there an equivalent to Visual Basic‘s On Error Resume Next for C++ where by if an error occurs code continues to execute without prompting user?
Ideally, would like to catch any exceptions and log them to log.txt file and examined later instead of having exceptions abruptly exiting code.
Visual Basic:
For i = 1 to 100
On Error Resume Next
ReturnedOpcVal[i] = OPC.ReadTag(“Server.Path.Tag” & "TagName_" & Cstr(i))
Next i
C++, I’m thinking this:
Try {
// Try looping through code here
For (i = 1; i <= 100; i++)
{
// Read OPC tags code here
}
}
Catch(…)
{
// log error to file code
}
There's nothing like that. You'd have to write your code to catch and swallow the exception. Something like this:
for(int i=1; i<=100; i++)
{
try
{
// Read OPC tags code here
}
catch(...)
{
// Log something
}
}
This would be the equivalent of the VB code:
for (i = 1; i <= 100; i++)
{
try {
// Read OPC tags code here
catch(…)
{
}
}
}
but you may want to wrap another try catch block round the whole lot as well.