FreeCAD

Форк
0
/
PropertyFile.cpp 
628 строк · 21.4 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2008 Jürgen Riegel <juergen.riegel@web.de>              *
3
 *                                                                         *
4
 *   This file is part of the FreeCAD CAx development system.              *
5
 *                                                                         *
6
 *   This library is free software; you can redistribute it and/or         *
7
 *   modify it under the terms of the GNU Library General Public           *
8
 *   License as published by the Free Software Foundation; either          *
9
 *   version 2 of the License, or (at your option) any later version.      *
10
 *                                                                         *
11
 *   This library  is distributed in the hope that it will be useful,      *
12
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14
 *   GNU Library General Public License for more details.                  *
15
 *                                                                         *
16
 *   You should have received a copy of the GNU Library General Public     *
17
 *   License along with this library; see the file COPYING.LIB. If not,    *
18
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
19
 *   Suite 330, Boston, MA  02111-1307, USA                                *
20
 *                                                                         *
21
 ***************************************************************************/
22

23

24
#include "PreCompiled.h"
25

26
#include <Base/Console.h>
27
#include <Base/PyObjectBase.h>
28
#include <Base/Reader.h>
29
#include <Base/Stream.h>
30
#include <Base/Writer.h>
31
#include <Base/Uuid.h>
32

33
#include "PropertyFile.h"
34
#include "Document.h"
35
#include "DocumentObject.h"
36
#include "PropertyContainer.h"
37

38

39
using namespace App;
40
using namespace Base;
41
using namespace std;
42

43

44

45
//**************************************************************************
46
// PropertyFileIncluded
47
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
48

49
TYPESYSTEM_SOURCE(App::PropertyFileIncluded , App::Property)
50

51

52
PropertyFileIncluded::PropertyFileIncluded() = default;
53

54
PropertyFileIncluded::~PropertyFileIncluded()
55
{
56
    // clean up
57
    if (!_cValue.empty()) {
58
        Base::FileInfo file(_cValue.c_str());
59
        file.setPermissions(Base::FileInfo::ReadWrite);
60
        file.deleteFile();
61
    }
62
}
63

64
void PropertyFileIncluded::aboutToSetValue()
65
{
66
    // This is a trick to check in Copy() if it is called
67
    // directly from outside or by the Undo/Redo mechanism.
68
    // In the latter case it is sufficient to rename the file
69
    // because another file will be assigned afterwards.
70
    // If Copy() is directly called (e.g. to copy the file to
71
    // another document) a copy of the file needs to be created.
72
    // This copy will be deleted again in the class destructor.
73
    this->StatusBits.set(10);
74
    Property::aboutToSetValue();
75
    this->StatusBits.reset(10);
76
}
77

78
std::string PropertyFileIncluded::getDocTransientPath() const
79
{
80
    std::string path;
81
    PropertyContainer *co = getContainer();
82
    if (co->isDerivedFrom(DocumentObject::getClassTypeId())) {
83
        path = static_cast<DocumentObject*>(co)->getDocument()->TransientDir.getValue();
84
        std::replace(path.begin(), path.end(), '\\', '/');
85
    }
86
    return path;
87
}
88

89
std::string PropertyFileIncluded::getUniqueFileName(const std::string& path, const std::string& filename) const
90
{
91
    Base::Uuid uuid;
92
    Base::FileInfo fi(path + "/" + filename);
93
    while (fi.exists()) {
94
        fi.setFile(path + "/" + filename + "." + uuid.getValue());
95
    }
96

97
    return fi.filePath();
98
}
99

100
std::string PropertyFileIncluded::getExchangeTempFile() const
101
{
102
    return Base::FileInfo::getTempFileName(Base::FileInfo
103
        (getValue()).fileName().c_str(), getDocTransientPath().c_str());
104
}
105

106
std::string PropertyFileIncluded::getOriginalFileName() const
107
{
108
    return _OriginalName;
109
}
110

111
void PropertyFileIncluded::setValue(const char* sFile, const char* sName)
112
{
113
    if (sFile && sFile[0] != '\0') {
114
        if (_cValue == sFile)
115
            throw Base::FileSystemError("Not possible to set the same file!");
116

117
        // keep the path to the original file
118
        _OriginalName = sFile;
119

120
        std::string pathTrans = getDocTransientPath();
121
        Base::FileInfo file(sFile);
122
        std::string path = file.dirPath();
123
        if (!file.exists()) {
124
            std::stringstream str;
125
            str << "File " << file.filePath() << " does not exist.";
126
            throw Base::FileSystemError(str.str());
127
        }
128

129
        aboutToSetValue(); // undo/redo by moving the file away with temp name
130

131
        // remove old file (if not moved by undo)
132
        Base::FileInfo value(_cValue);
133
        std::string pathAct = value.dirPath();
134
        if (value.exists()) {
135
            value.setPermissions(Base::FileInfo::ReadWrite);
136
            value.deleteFile();
137
        }
138

139
        // if a special name given, use this instead
140
        if (sName) {
141
            Base::FileInfo fi(pathTrans + "/" + sName);
142
            if (fi.exists()) {
143
                // if a file with this name already exists search for a new one
144
                std::string dir = pathTrans;
145
                std::string fnp = fi.fileNamePure();
146
                std::string ext = fi.extension();
147
                int i=0;
148
                do {
149
                    i++;
150
                    std::stringstream str;
151
                    str << dir << "/" << fnp << i;
152
                    if (!ext.empty())
153
                        str << "." << ext;
154
                    fi.setFile(str.str());
155
                }
156
                while (fi.exists());
157

158
                _cValue = fi.filePath();
159
                _BaseFileName = fi.fileName();
160
            }
161
            else {
162
                _cValue = pathTrans + "/" + sName;
163
                _BaseFileName = sName;
164
            }
165
        }
166
        else if (value.fileName().empty()) {
167
            _cValue = pathTrans + "/" + file.fileName();
168
            _BaseFileName = file.fileName();
169
        }
170

171
        // The following applies only on files that are inside the transient
172
        // directory:
173
        // When a file is read-only it is supposed to be assigned to a
174
        // PropertyFileIncluded instance. In this case we must copy the
175
        // file because otherwise the above instance loses its data.
176
        // If the file is writable it is supposed to be of free use and
177
        // it can be simply renamed.
178

179
        // if the file is already in transient dir of the document, just use it
180
        if (path == pathTrans && file.isWritable()) {
181
            bool done = file.renameFile(_cValue.c_str());
182
            if (!done) {
183
                std::stringstream str;
184
                str << "Cannot rename file " << file.filePath() << " to " << _cValue;
185
                throw Base::FileSystemError(str.str());
186
            }
187

188
            // make the file read-only
189
            Base::FileInfo dst(_cValue);
190
            dst.setPermissions(Base::FileInfo::ReadOnly);
191
        }
192
        // otherwise copy from origin location 
193
        else {
194
            // if file already exists in transient dir make a new unique name
195
            Base::FileInfo fi(_cValue);
196
            if (fi.exists()) {
197
                // if a file with this name already exists search for a new one
198
                std::string dir = fi.dirPath();
199
                std::string fnp = fi.fileNamePure();
200
                std::string ext = fi.extension();
201
                int i=0;
202
                do {
203
                    i++;
204
                    std::stringstream str;
205
                    str << dir << "/" << fnp << i;
206
                    if (!ext.empty())
207
                        str << "." << ext;
208
                    fi.setFile(str.str());
209
                }
210
                while (fi.exists());
211

212
                _cValue = fi.filePath();
213
                _BaseFileName = fi.fileName();
214
            }
215

216
            bool done = file.copyTo(_cValue.c_str());
217
            if (!done) {
218
                std::stringstream str;
219
                str << "Cannot copy file from " << file.filePath() << " to " << _cValue;
220
                throw Base::FileSystemError(str.str());
221
            }
222

223
            // make the file read-only
224
            Base::FileInfo dst(_cValue);
225
            dst.setPermissions(Base::FileInfo::ReadOnly);
226
        }
227

228
        hasSetValue();
229
    }
230
}
231

232
const char* PropertyFileIncluded::getValue() const
233
{
234
     return _cValue.c_str();
235
}
236

237
PyObject *PropertyFileIncluded::getPyObject()
238
{
239
    PyObject *p = PyUnicode_DecodeUTF8(_cValue.c_str(),_cValue.size(),nullptr);
240
    if (!p) {
241
        throw Base::UnicodeError("PropertyFileIncluded: UTF-8 conversion failure");
242
    }
243
    return p;
244
}
245

246
namespace App {
247
const char* getNameFromFile(PyObject* value)
248
{
249
    const char* string = nullptr;
250
    PyObject *oname = PyObject_GetAttrString (value, "name");
251
    if (oname) {
252
        if (PyUnicode_Check (oname)) {
253
            string = PyUnicode_AsUTF8 (oname);
254
        }
255
        else if (PyBytes_Check (oname)) {
256
            string = PyBytes_AsString (oname);
257
        }
258
        Py_DECREF (oname);
259
    }
260

261
    if (!string)
262
        throw Base::TypeError("Unable to get filename");
263
    return string;
264
}
265

266

267

268
bool isIOFile(PyObject* file)
269
{
270
    PyObject* io = PyImport_ImportModule("io");
271
    PyObject* IOBase_Class = PyObject_GetAttrString(io, "IOBase");
272
    bool isFile = PyObject_IsInstance(file, IOBase_Class);
273
    Py_DECREF(IOBase_Class);
274
    Py_DECREF(io);
275
    return isFile;
276
}
277
}
278

279
void PropertyFileIncluded::setPyObject(PyObject *value)
280
{
281
    if (PyUnicode_Check(value)) {
282
        std::string string = PyUnicode_AsUTF8(value);
283
        setValue(string.c_str());
284
    }
285
    else if (PyBytes_Check(value)) {
286
        std::string string = PyBytes_AsString(value);
287
        setValue(string.c_str());
288
    }
289
    else if (isIOFile(value)){
290
        std::string string = getNameFromFile(value);
291
        setValue(string.c_str());
292
    }
293
    else if (PyTuple_Check(value)) {
294
        if (PyTuple_Size(value) != 2)
295
            throw Base::TypeError("Tuple needs size of (filePath,newFileName)"); 
296
        PyObject* file = PyTuple_GetItem(value,0);
297
        PyObject* name = PyTuple_GetItem(value,1);
298

299
        // decoding file
300
        std::string fileStr;
301
        if (PyUnicode_Check(file)) {
302
            fileStr = PyUnicode_AsUTF8(file);
303
        }
304
        else if (PyBytes_Check(file)) {
305
            fileStr = PyBytes_AsString(file);
306
        }
307
        else if (isIOFile(value)) {
308
            fileStr = getNameFromFile(file);
309
        }
310
        else {
311
            std::string error = std::string("First item in tuple must be a file or string, not ");
312
            error += file->ob_type->tp_name;
313
            throw Base::TypeError(error);
314
        }
315

316
        // decoding name
317
        std::string nameStr;
318
        if (PyUnicode_Check(name)) {
319
            nameStr = PyUnicode_AsUTF8(name);
320
        }
321
        else if (PyBytes_Check(name)) {
322
            nameStr = PyBytes_AsString(name);
323
        }
324
        else if (isIOFile(value)) {
325
            nameStr = getNameFromFile(name);
326
        }
327
        else {
328
            std::string error = std::string("Second item in tuple must be a string, not ");
329
            error += name->ob_type->tp_name;
330
            throw Base::TypeError(error);
331
        }
332

333
        setValue(fileStr.c_str(),nameStr.c_str());
334
    }
335
    else if (PyDict_Check(value)) {
336
        Py::Dict dict(value);
337
        if (dict.hasKey("filter")) {
338
            setFilter(Py::String(dict.getItem("filter")));
339
        }
340
        if (dict.hasKey("filename")) {
341
            std::string string = static_cast<std::string>(Py::String(dict.getItem("filename")));
342
            setValue(string.c_str());
343
        }
344
    }
345
    else {
346
        std::string error = std::string("Type must be string or file, not ");
347
        error += value->ob_type->tp_name;
348
        throw Base::TypeError(error);
349
    }
350
}
351

352
void PropertyFileIncluded::Save (Base::Writer &writer) const
353
{
354
    // when saving a document under a new file name the transient directory
355
    // name changes and thus the stored file name doesn't work any more.
356
    if (!_cValue.empty() && !Base::FileInfo(_cValue).exists()) {
357
        Base::FileInfo fi(getDocTransientPath() + "/" + _BaseFileName);
358
        if (fi.exists())
359
            _cValue = fi.filePath();
360
    }
361

362
    if (writer.isForceXML()) {
363
        if (!_cValue.empty()) {
364
            Base::FileInfo file(_cValue.c_str());
365
            writer.Stream() << writer.ind() << "<FileIncluded data=\""
366
                            << file.fileName() << "\">" << std::endl;
367
            // write the file in the XML stream
368
            writer.incInd();
369
            writer.insertBinFile(_cValue.c_str());
370
            writer.decInd();
371
            writer.Stream() << writer.ind() <<"</FileIncluded>" << endl;
372
        }
373
        else {
374
            writer.Stream() << writer.ind() << "<FileIncluded data=\"\"/>" << std::endl;
375
        }
376
    }
377
    else {
378
        // instead initiate an extra file 
379
        if (!_cValue.empty()) {
380
            Base::FileInfo file(_cValue.c_str());
381
            std::string filename = writer.addFile(file.fileName().c_str(), this);
382
            filename = encodeAttribute(filename);
383
            writer.Stream() << writer.ind() << "<FileIncluded file=\""
384
                            << filename << "\"/>" << std::endl;
385
        }
386
        else {
387
            writer.Stream() << writer.ind() << "<FileIncluded file=\"\"/>" << std::endl;
388
        }
389
    }
390
}
391

392
void PropertyFileIncluded::Restore(Base::XMLReader &reader)
393
{
394
    reader.readElement("FileIncluded");
395
    if (reader.hasAttribute("file")) {
396
        string file (reader.getAttribute("file") );
397
        if (!file.empty()) {
398
            // initiate a file read
399
            reader.addFile(file.c_str(),this);
400
            // is in the document transient path
401
            aboutToSetValue();
402
            _cValue = getDocTransientPath() + "/" + file;
403
            _BaseFileName = file;
404
            hasSetValue();
405
        }
406
    }
407
    // section is XML stream
408
    else if (reader.hasAttribute("data")) {
409
        string file (reader.getAttribute("data") );
410
        if (!file.empty()) {
411
            // is in the document transient path
412
            aboutToSetValue();
413
            _cValue = getDocTransientPath() + "/" + file;
414
            reader.readBinFile(_cValue.c_str());
415
            reader.readEndElement("FileIncluded");
416
            _BaseFileName = file;
417
            // set read-only after restoring the file
418
            Base::FileInfo fi(_cValue.c_str());
419
            fi.setPermissions(Base::FileInfo::ReadOnly);
420
            hasSetValue();
421
        }
422
    }
423
}
424

425
void PropertyFileIncluded::SaveDocFile (Base::Writer &writer) const
426
{
427
    Base::ifstream from(Base::FileInfo(_cValue.c_str()), std::ios::in | std::ios::binary);
428
    if (!from) {
429
        std::stringstream str;
430
        str << "PropertyFileIncluded::SaveDocFile(): "
431
            << "File '" << _cValue << "' in transient directory doesn't exist.";
432
        throw Base::FileSystemError(str.str());
433
    }
434

435
    // copy plain data
436
    unsigned char c;
437
    std::ostream& to = writer.Stream();
438
    while (from.get((char&)c)) {
439
        to.put((char)c);
440
    }
441
}
442

443
void PropertyFileIncluded::RestoreDocFile(Base::Reader &reader)
444
{
445
    Base::FileInfo fi(_cValue.c_str());
446
    if (fi.exists() && !fi.isWritable()) {
447
        // This happens when an object is being restored and tries to reference the
448
        // same file of another object (e.g. for copy&paste of objects inside the same document).
449
        return;
450
    }
451
    Base::ofstream to(fi, std::ios::out | std::ios::binary);
452
    if (!to) {
453
        std::stringstream str;
454
        str << "PropertyFileIncluded::RestoreDocFile(): "
455
            << "File '" << _cValue << "' in transient directory cannot be created.";
456
        throw Base::FileSystemError(str.str());
457
    }
458

459
    // copy plain data
460
    aboutToSetValue();
461
    unsigned char c;
462
    while (reader.get((char&)c)) {
463
        to.put((char)c);
464
    }
465
    to.close();
466

467
    // set read-only after restoring the file
468
    fi.setPermissions(Base::FileInfo::ReadOnly);
469
    hasSetValue();
470
}
471

472
Property *PropertyFileIncluded::Copy() const
473
{
474
    std::unique_ptr<PropertyFileIncluded> prop(new PropertyFileIncluded());
475

476
    // remember the base name
477
    prop->_BaseFileName = _BaseFileName;
478

479
    Base::FileInfo file(_cValue);
480
    if (file.exists()) {
481
        // create a new name in the document transient directory
482
        Base::FileInfo newName(getUniqueFileName(file.dirPath(), file.fileName()));
483
        if (this->StatusBits.test(10)) {
484
            // rename the file
485
            bool done = file.renameFile(newName.filePath().c_str());
486
            if (!done) {
487
                std::stringstream str;
488
                str << "PropertyFileIncluded::Copy(): "
489
                    << "Renaming the file '" << file.filePath() << "' to '"
490
                    << newName.filePath() << "' failed.";
491
                throw Base::FileSystemError(str.str());
492
            }
493
        }
494
        else {
495
            // copy the file
496
            bool done = file.copyTo(newName.filePath().c_str());
497
            if (!done) {
498
                std::stringstream str;
499
                str << "PropertyFileIncluded::Copy(): "
500
                    << "Copying the file '" << file.filePath() << "' to '"
501
                    << newName.filePath() << "' failed.";
502
                throw Base::FileSystemError(str.str());
503
            }
504
        }
505

506
        // remember the new name for the Undo
507
        Base::Console().Log("Copy '%s' to '%s'\n",_cValue.c_str(),newName.filePath().c_str());
508
        prop->_cValue = newName.filePath().c_str();
509

510
        // make backup files writable to avoid copying them again on undo/redo
511
        newName.setPermissions(Base::FileInfo::ReadWrite);
512
    }
513

514
    return prop.release();
515
}
516

517
void PropertyFileIncluded::Paste(const Property &from)
518
{
519
    aboutToSetValue();
520
    const PropertyFileIncluded &prop = dynamic_cast<const PropertyFileIncluded&>(from);
521
    // make sure that source and destination file are different
522
    if (_cValue != prop._cValue) {
523
        // delete old file (if still there)
524
        Base::FileInfo fi(_cValue);
525
        fi.setPermissions(Base::FileInfo::ReadWrite);
526
        fi.deleteFile();
527

528
        // get path to destination which can be the transient directory
529
        // of another document
530
        std::string pathTrans = getDocTransientPath();
531
        Base::FileInfo fiSrc(prop._cValue);
532
        Base::FileInfo fiDst(pathTrans + "/" + prop._BaseFileName);
533
        std::string path = fiSrc.dirPath();
534

535
        if (fiSrc.exists()) {
536
            fiDst.setFile(getUniqueFileName(fiDst.dirPath(), fiDst.fileName()));
537

538
            // if the file is already in transient dir of the document, just use it
539
            if (path == pathTrans) {
540
                if (!fiSrc.renameFile(fiDst.filePath().c_str())) {
541
                    std::stringstream str;
542
                    str << "PropertyFileIncluded::Paste(): "
543
                        << "Renaming the file '" << fiSrc.filePath() << "' to '"
544
                        << fiDst.filePath() << "' failed.";
545
                    throw Base::FileSystemError(str.str());
546
                }
547
            }
548
            else {
549
                if (!fiSrc.copyTo(fiDst.filePath().c_str())) {
550
                    std::stringstream str;
551
                    str << "PropertyFileIncluded::Paste(): "
552
                        << "Copying the file '" << fiSrc.filePath() << "' to '"
553
                        << fiDst.filePath() << "' failed.";
554
                    throw Base::FileSystemError(str.str());
555
                }
556
            }
557

558
            // set the file again read-only
559
            fiDst.setPermissions(Base::FileInfo::ReadOnly);
560
            _cValue = fiDst.filePath();
561
        }
562
        else {
563
            _cValue.clear();
564
        }
565

566
        // set the base name
567
        _BaseFileName = prop._BaseFileName;
568
    }
569
    hasSetValue();
570
}
571

572
unsigned int PropertyFileIncluded::getMemSize () const
573
{
574
    unsigned int mem = Property::getMemSize();
575
    mem += _cValue.size();
576
    mem += _BaseFileName.size();
577
    return mem;
578
}
579

580
void PropertyFileIncluded::setFilter(std::string filter)
581
{
582
    m_filter = std::move(filter);
583
}
584

585
std::string PropertyFileIncluded::getFilter() const
586
{
587
    return m_filter;
588
}
589

590
//**************************************************************************
591
// PropertyFile
592
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
593

594
TYPESYSTEM_SOURCE(App::PropertyFile , App::PropertyString)
595

596
PropertyFile::PropertyFile()
597
{
598
    m_filter = "";
599
}
600

601
PropertyFile::~PropertyFile() = default;
602

603
void PropertyFile::setFilter(const std::string f)
604
{
605
    m_filter = f;
606
}
607

608
std::string PropertyFile::getFilter() const
609
{
610
    return m_filter;
611
}
612

613
void PropertyFile::setPyObject(PyObject *value)
614
{
615
    if (PyDict_Check(value)) {
616
        Py::Dict dict(value);
617
        if (dict.hasKey("filter")) {
618
            setFilter(Py::String(dict.getItem("filter")));
619
        }
620
        if (dict.hasKey("filename")) {
621
            std::string string = static_cast<std::string>(Py::String(dict.getItem("filename")));
622
            setValue(string.c_str());
623
        }
624
    }
625
    else {
626
        PropertyString::setPyObject(value);
627
    }
628
}
629

630

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.