SReader.cpp 16.8 KB
Newer Older
1
/* ***** BEGIN LICENSE BLOCK *****
2
 * FW4SPL - Copyright (C) IRCAD, 2009-2017.
3
4
5
6
 * Distributed under the terms of the GNU Lesser General Public License (LGPL) as
 * published by the Free Software Foundation.
 * ****** END LICENSE BLOCK ****** */

7
#include "ioAtoms/SReader.hpp"
8

9
10
#include <fwAtomConversion/convert.hpp>

11
#include <fwAtomsBoostIO/Reader.hpp>
12
#include <fwAtomsBoostIO/types.hpp>
13

14
#include <fwAtomsFilter/factory/new.hpp>
15
#include <fwAtomsFilter/IFilter.hpp>
16

17
18
19
#include <fwAtomsPatch/PatchingManager.hpp>

#include <fwCom/Signal.hxx>
20

21
#include <fwData/Array.hpp>
22
23
#include <fwData/Composite.hpp>
#include <fwData/location/Folder.hpp>
24
#include <fwData/location/SingleFile.hpp>
25

26
#include <fwDataTools/helper/Composite.hpp>
27
28
29
30
31

#include <fwGui/Cursor.hpp>
#include <fwGui/dialog/LocationDialog.hpp>
#include <fwGui/dialog/MessageDialog.hpp>

32
33
34
#include <fwJobs/Aggregator.hpp>
#include <fwJobs/Job.hpp>

35
36
37
38
#include <fwServices/macros.hpp>

#include <fwZip/ReadDirArchive.hpp>
#include <fwZip/ReadZipArchive.hpp>
39

40
#include <boost/algorithm/string/join.hpp>
41
42
#include <boost/assign/list_of.hpp>
#include <boost/filesystem/path.hpp>
43
44
45
46

namespace ioAtoms
{

julien.waechter's avatar
julien.waechter committed
47
fwServicesRegisterMacro( ::io::IReader, ::ioAtoms::SReader, ::fwData::Object );
48

49
50
static const ::fwCom::Signals::SignalKeyType JOB_CREATED_SIGNAL = "jobCreated";

51
52
const SReader::FileExtension2NameType SReader::s_EXTENSIONS
    = ::boost::assign::map_list_of(".xml", "XML")
julien.waechter's avatar
julien.waechter committed
53
54
          (".xmlz", "Zipped XML")
          (".json", "JSON")
55
          (".jsonz", "Zipped JSON");
56

57
58
//-----------------------------------------------------------------------------

59
SReader::SReader() :
julien.waechter's avatar
julien.waechter committed
60
    m_useAtomsPatcher(false),
61
62
63
    m_context("Undefined"),
    m_version("Undefined"),
    m_filter("")
64
{
65
66
    m_sigJobCreated = newSignal< JobCreatedSignalType >( JOB_CREATED_SIGNAL );

67
    for(SReader::FileExtension2NameType::value_type ext :  s_EXTENSIONS)
68
69
70
71
    {
        m_allowedExts.insert(m_allowedExts.end(), ext.first);
    }
}
72
73
74

//-----------------------------------------------------------------------------

75
void SReader::starting() throw(::fwTools::Failed)
julien.waechter's avatar
julien.waechter committed
76
77
{
}
78
79
80
81

//-----------------------------------------------------------------------------

void SReader::stopping() throw(::fwTools::Failed)
julien.waechter's avatar
julien.waechter committed
82
83
{
}
84
85
86
87
88
89
90
91
92

//-----------------------------------------------------------------------------

void SReader::configuring() throw(::fwTools::Failed)
{
    SLM_TRACE_FUNC();

    ::io::IReader::configuring();

julien.waechter's avatar
julien.waechter committed
93
    typedef SPTR (::fwRuntime::ConfigurationElement) ConfigurationElement;
94
95
    typedef std::vector < ConfigurationElement >    ConfigurationElementContainer;

96
97
98
99
    m_customExts.clear();
    m_allowedExtLabels.clear();

    ConfigurationElementContainer customExtsList = m_configuration->find("archive");
100
    for(ConfigurationElement archive :  customExtsList)
101
102
103
104
105
106
    {
        const std::string& backend = archive->getAttributeValue("backend");
        SLM_ASSERT("No backend attribute given in archive tag", backend != "");
        SLM_ASSERT("Unsupported backend '" + backend + "'", s_EXTENSIONS.find("." + backend) != s_EXTENSIONS.end());

        ConfigurationElementContainer exts = archive->find("extension");
107
        for(ConfigurationElement ext :  exts)
108
109
110
111
112
        {
            const std::string& extension = ext->getValue();
            SLM_ASSERT("No extension given for backend '" + backend + "'", !extension.empty());
            SLM_ASSERT("Extension must begin with '.'", extension[0] == '.');

julien.waechter's avatar
julien.waechter committed
113
            m_customExts[extension]       = backend;
114
115
116
117
            m_allowedExtLabels[extension] = ext->getAttributeValue("label");
        }
    }

118
119
120
121
122
    ConfigurationElementContainer extensionsList = m_configuration->find("extensions");
    SLM_ASSERT("The <extensions> element can be set at most once.", extensionsList.size() <= 1);

    if(extensionsList.size() == 1)
    {
123
124
        m_allowedExts.clear();

125
        ConfigurationElementContainer extensions = extensionsList.at(0)->find("extension");
126
        for(ConfigurationElement extension :  extensions)
127
128
129
        {
            const std::string& ext = extension->getValue();

130
            // The extension must be found either in custom extensions list or in known extensions
julien.waechter's avatar
julien.waechter committed
131
            FileExtension2NameType::const_iterator itKnown  = s_EXTENSIONS.find(ext);
132
133
134
135
            FileExtension2NameType::const_iterator itCustom = m_customExts.find(ext);

            const bool extIsKnown = (itKnown != SReader::s_EXTENSIONS.end() || itCustom != m_customExts.end());
            SLM_ASSERT("Extension '" + ext + "' is not allowed in configuration", extIsKnown);
136

137
            if(extIsKnown)
138
139
            {
                m_allowedExts.insert(m_allowedExts.end(), ext);
140
                m_allowedExtLabels[ext] = extension->getAttributeValue("label");
141
142
143
            }
        }
    }
144
145
146
147
    else
    {
        m_allowedExts.clear();

148
        for(FileExtension2NameType::value_type ext :  m_customExts)
149
150
151
152
        {
            m_allowedExts.insert(m_allowedExts.end(), ext.first);
        }

153
        for(SReader::FileExtension2NameType::value_type ext :  SReader::s_EXTENSIONS)
154
155
156
157
158
        {
            m_allowedExts.insert(m_allowedExts.end(), ext.first);
            m_allowedExtLabels[ext.first] = ext.second;
        }
    }
159
160

    ConfigurationElementContainer inject = m_configuration->find("inject");
161
    SLM_ASSERT("The <inject> element can be set at most once.", inject.size() <= 1);
162
163
164
165
166
    if (inject.size() == 1)
    {
        m_inject = inject.at(0)->getValue();
    }

167
168
169
170
171
172
173
    ConfigurationElementContainer filter = m_configuration->find("filter");
    SLM_ASSERT("The <filter> element can be set at most once.", filter.size() <= 1);
    if (filter.size() == 1)
    {
        m_filter = filter.at(0)->getValue();
    }

174
    ConfigurationElementContainer uuidPolicy = m_configuration->find("uuidPolicy");
175
    SLM_ASSERT("The <uuidPolicy> element can be set at most once.", uuidPolicy.size() <= 1);
176
177
178
179
    if (uuidPolicy.size() == 1)
    {
        m_uuidPolicy = uuidPolicy.at(0)->getValue();
        SLM_ASSERT("Unknown policy : '"
180
181
                   + m_uuidPolicy +
                   "', available policies : 'Strict','Change' or 'Reuse'.",
182
183
184
185
                   "Strict" == m_uuidPolicy || "Change" == m_uuidPolicy || "Reuse" == m_uuidPolicy );

        SLM_ASSERT("'Reuse' policy is available only with inject mode",
                   ("Reuse" == m_uuidPolicy && !m_inject.empty()) || "Reuse" != m_uuidPolicy
julien.waechter's avatar
julien.waechter committed
186
                   );
187
188
    }

189
190
191
192
    ConfigurationElementContainer patcher = m_configuration->find("patcher");
    SLM_ASSERT("The <patcher> element can be set at most once.", patcher.size() <= 1 );
    if (patcher.size() == 1)
    {
julien.waechter's avatar
julien.waechter committed
193
194
        m_context         = patcher.at(0)->getExistingAttributeValue("context");
        m_version         = patcher.at(0)->getExistingAttributeValue("version");
195
196
197
        m_useAtomsPatcher = true;
    }

198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
}

//-----------------------------------------------------------------------------

void SReader::updating() throw(::fwTools::Failed)
{
    if(this->hasLocationDefined())
    {
        ::fwData::Object::sptr data = this->getObject< ::fwData::Object >();

        ::fwGui::Cursor cursor;
        cursor.setCursor(::fwGui::ICursor::BUSY);

        try
        {
julien.waechter's avatar
julien.waechter committed
213
            const ::boost::filesystem::path& filePath  = this->getFile();
214
            const ::boost::filesystem::path folderPath = filePath.parent_path();
julien.waechter's avatar
julien.waechter committed
215
216
            const ::boost::filesystem::path filename   = filePath.filename();
            std::string extension                      = ::boost::filesystem::extension(filePath);
217
218
219

            FW_RAISE_IF( "Unable to guess file format (missing extension)", extension.empty() );

220
221
222
223
224
            if(m_customExts.find(extension) != m_customExts.end())
            {
                extension = "." + m_customExts[extension];
            }

225
            ::fwAtoms::Object::sptr atom;
226

227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
            const unsigned int progressBarOffset = 10;

            // Reading file : job 1
            ::fwJobs::Job::sptr fileReadingJob = ::fwJobs::Job::New("Reading " + extension + " file",
                                                                    [ =, &atom](::fwJobs::Job& runningJob)
                {
                    runningJob.doneWork(progressBarOffset);

                    // Read atom
                    ::fwZip::IReadArchive::sptr readArchive;
                    ::boost::filesystem::path archiveRootName;
                    ::fwAtomsBoostIO::FormatType format = ::fwAtomsBoostIO::UNSPECIFIED;

                    if ( extension == ".json" )
                    {
                        readArchive = ::fwZip::ReadDirArchive::New(folderPath.string());
                        archiveRootName = filename;
                        format = ::fwAtomsBoostIO::JSON;
                    }
                    else if ( extension == ".jsonz" )
                    {
                        readArchive = ::fwZip::ReadZipArchive::New(filePath.string());
                        archiveRootName = "root.json";
                        format = ::fwAtomsBoostIO::JSON;
                    }
                    else if ( extension == ".xml" )
                    {
                        readArchive = ::fwZip::ReadDirArchive::New(folderPath.string());
                        archiveRootName = filename;
                        format = ::fwAtomsBoostIO::XML;
                    }
                    else if ( extension == ".xmlz" )
                    {
                        readArchive = ::fwZip::ReadZipArchive::New(filePath.string());
                        archiveRootName = "root.xml";
                        format = ::fwAtomsBoostIO::XML;
                    }
                    else
                    {
                        FW_RAISE( "This file extension '" << extension << "' is not managed" );
                    }

                    ::fwAtomsBoostIO::Reader reader;
                    atom = ::fwAtoms::Object::dynamicCast( reader.read( readArchive, archiveRootName, format ) );

                    FW_RAISE_IF( "Invalid atoms file :'" << filePath << "'", !atom );

                    runningJob.doneWork(progressBarOffset);

                    runningJob.done();

                }, m_associatedWorker);

            // patching atom : job 2
            ::fwJobs::Job::sptr patchingJob = ::fwJobs::Job::New("Reading " + extension + " file",
                                                                 [ =, &atom](::fwJobs::Job& runningJob)
                {
                    if(runningJob.cancelRequested())
                    {
                        return;
                    }

                    runningJob.doneWork(progressBarOffset);

                    /// patch atom
                    if ( m_useAtomsPatcher )
                    {
                        FW_RAISE_IF( "Unable to load data, found '" << atom->getMetaInfo("context")
                                                                    << "' context, but '" << m_context <<
                                     "' was excepted.",
                                     atom->getMetaInfo("context") != m_context);

                        ::fwAtomsPatch::PatchingManager globalPatcher(atom);
                        atom = globalPatcher.transformTo( m_version );
                    }

                    if(!m_filter.empty())
                    {
                        ::fwAtomsFilter::IFilter::sptr filter = ::fwAtomsFilter::factory::New(m_filter);
                        OSLM_ASSERT("Failed to create IFilter implementation '" << m_filter << "'", filter);
                        filter->apply(atom);
                    }
                    runningJob.done();
                }, m_associatedWorker);
311

julien.waechter's avatar
julien.waechter committed
312
            ::fwData::Object::sptr newData;
313

314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
            // convert to fwData : job 3
            ::fwJobs::Job::sptr atomToDataJob = ::fwJobs::Job::New("Reading " + extension + " file",
                                                                   [ =, &newData, &atom](::fwJobs::Job& runningJob)
                {
                    runningJob.doneWork(progressBarOffset);
                    if(runningJob.cancelRequested())
                    {
                        return;
                    }
                    if("Strict" == m_uuidPolicy)
                    {
                        newData = ::fwAtomConversion::convert(atom, ::fwAtomConversion::AtomVisitor::StrictPolicy());
                    }
                    else if("Reuse" == m_uuidPolicy)
                    {
                        newData = ::fwAtomConversion::convert(atom, ::fwAtomConversion::AtomVisitor::ReusePolicy());
                    }
                    else
                    {
                        newData = ::fwAtomConversion::convert(atom, ::fwAtomConversion::AtomVisitor::ChangePolicy());
                    }

                    runningJob.done();
                }, m_associatedWorker);

            ::fwJobs::Aggregator::sptr jobs = ::fwJobs::Aggregator::New(extension + " reader");
            jobs->add(fileReadingJob);
            jobs->add(patchingJob);
            jobs->add(atomToDataJob);

            m_sigJobCreated->emit(jobs);

            jobs->run().get();

            if(jobs->getState() == ::fwJobs::IJob::CANCELED)
349
            {
350
                return;
351
352
            }

julien.waechter's avatar
julien.waechter committed
353
            FW_RAISE_IF( "Unable to load '" << filePath << "' : invalid data.", !newData );
354
355
356

            if(m_inject.empty())
            {
357
                FW_RAISE_IF( "Unable to load '" << filePath
julien.waechter's avatar
julien.waechter committed
358
359
360
                                                << "' : trying to load a '" << newData->getClassname()
                                                << "' where a '" << data->getClassname() << "' was expected",
                             newData->getClassname() != data->getClassname() );
361

362
363
364
                // Workaround to read a fwData::Array.
                // The shallowCopy of a fwData::Array is not allowed due to unknown buffer owner.
                // So in the case of reading an Array we swap buffers.
365
                if(newData->getClassname() == ::fwData::Array::classname())
366
367
368
369
370
371
372
373
                {
                    ::fwData::Array::dynamicCast(data)->swap( ::fwData::Array::dynamicCast(newData) );
                }
                else
                {
                    data->shallowCopy(newData);
                }

374
375
376
377
378
379
            }
            else
            {
                ::fwData::Composite::sptr composite = ::fwData::Composite::dynamicCast(data);
                SLM_ASSERT("Inject mode works only on a Composite object", composite );

380
                ::fwDataTools::helper::Composite helper(composite);
381
                helper.add(m_inject, newData);
Emilie Harquel's avatar
Emilie Harquel committed
382
                helper.notify();
383
384
385
386
            }

            this->notificationOfUpdate();
        }
387
        catch( std::exception& e )
388
389
390
        {
            OSLM_ERROR( e.what() );
            ::fwGui::dialog::MessageDialog::showMessageDialog("Atoms reader failed", e.what(),
julien.waechter's avatar
julien.waechter committed
391
                                                              ::fwGui::dialog::MessageDialog::CRITICAL);
392
393
394
395
        }
        catch( ... )
        {
            ::fwGui::dialog::MessageDialog::showMessageDialog("Atoms reader failed", "Aborting operation.",
julien.waechter's avatar
julien.waechter committed
396
                                                              ::fwGui::dialog::MessageDialog::CRITICAL);
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
        }

        cursor.setDefaultCursor();

    }
}

//-----------------------------------------------------------------------------

::io::IOPathType SReader::getIOPathType() const
{
    return ::io::FILE;
}

//------------------------------------------------------------------------------

void SReader::notificationOfUpdate()
{
Emilie Harquel's avatar
Emilie Harquel committed
415
416
    ::fwData::Object::sptr object = this->getObject();
    auto sig = object->signal< ::fwData::Object::ModifiedSignalType >(::fwData::Object::s_MODIFIED_SIG);
417
    {
Emilie Harquel's avatar
Emilie Harquel committed
418
419
        ::fwCom::Connection::Blocker block(sig->getConnection(m_slotUpdate));
        sig->asyncEmit();
420
    }
421
422
423
424
425
426
}

//-----------------------------------------------------------------------------

void SReader::configureWithIHM()
{
427
428
429
    static ::boost::filesystem::path _sDefaultPath;

    ::fwGui::dialog::LocationDialog dialogFile;
430
    dialogFile.setTitle(m_windowTitle.empty() ? "Enter file name" : m_windowTitle);
431
432
433
434
435
    dialogFile.setDefaultLocation( ::fwData::location::Folder::New(_sDefaultPath) );
    dialogFile.setType(::fwGui::dialog::ILocationDialog::SINGLE_FILE);
    dialogFile.setOption(::fwGui::dialog::ILocationDialog::READ);
    dialogFile.setOption(::fwGui::dialog::LocationDialog::FILE_MUST_EXIST);

436
    dialogFile.addFilter("Medical data", "*" + ::boost::algorithm::join(m_allowedExts, " *"));
437

438
    for(const std::string& ext :  m_allowedExts)
439
440
    {
        dialogFile.addFilter(m_allowedExtLabels[ext], "*" + ext);
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
    }

    ::fwData::location::SingleFile::sptr result
        = ::fwData::location::SingleFile::dynamicCast( dialogFile.show() );

    if (result)
    {
        _sDefaultPath = result->getPath();
        this->setFile( _sDefaultPath );
        dialogFile.saveDefaultLocation( ::fwData::location::Folder::New(_sDefaultPath.parent_path()) );
    }
    else
    {
        this->clearLocations();
    }
456
457
458
459
460
461
}

//-----------------------------------------------------------------------------

} // namespace ioAtoms