<commit_before>#include "parser.h"
#include <QFileInfoList>
#include <iostream>
#include <QString>
#include <QDebug>

Parser::Parser(int argc, char **argv)
    : itsArgc(argc)
    , itsArgv(argv)
    , itsDir(new QDir)
    , itsOptions(NONE)
    , itsFilters(QStringList() << "")
    , itsIn(stdin)
    , itsOut(stdout)
{
}

void Parser::applyOptions()
{
    parseOptions();
#ifdef DEBUG
    qDebug() << "getCollectedOptions() =" << getCollectedOptions();
    qDebug() << "(int)getCollectedOptions() =" << (int)getCollectedOptions();
#endif
    switcher(getCollectedOptions());
}

void Parser::parseToFile()
{
    dirFilters();

    if(!itsDir->exists(itsArgv[1]))
    {
        itsOut << "EROR: path to list don't exist\n";
        return;
    }

    itsDir->cd(itsArgv[1]);

    QFile file;
    QFileInfo info;

    file.setFileName(itsArgv[2]);
    info.setFile(file);

    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        itsOut << "ERROR opening file:" << file.fileName();
        return;
    }

    QTextStream out(&file);
    out << "ROOT DIR:\n" << itsArgv[1] << ":" << "\n";

    QString filePath = info.absoluteFilePath();

    if(itsOptions.testFlag(RECURSIVE))
    {
        recursive(filePath, out);
    }
    else
    {
        notRecursive(filePath, out);
    }

    QString p = info.filePath();
    QString n = info.fileName();

    itsOut << "\nSaving file: " << info.fileName()
           << " into "
           << p.remove(p.size() - n.size(), p.size() - 1)
           << "\n";

    file.close();
}

void Parser::parseToConsole()
{

    dirFilters();

    if(!itsDir->exists(itsArgv[1]))
    {
        itsOut << "EROR: path to list don't exist\n";
        return;
    }

    itsDir->cd(itsArgv[1]);
    itsOut << "ROOT DIR:\n" << itsArgv[1] << ":\n";

    if(itsOptions.testFlag(RECURSIVE))
    {
        recursive(itsDir->absolutePath());
    }
    else
    {
        notRecursive(itsDir->absolutePath());
    }
}

void Parser::help()
{
    itsOut << "usage:\t[--help]\n";
    itsOut << "   or:\t<path to list> [options]\n";
    itsOut << "   or:\t<path to list> <path and file name to save results> [options]\n";
    itsOut << "options:\n";
    itsOut << "\t-d\tshow directories\n";
    itsOut << "\t-h\thide output to console\n";
    itsOut << "\t-r\trecursively listing\n";
    itsOut << "\t-s\tshow hidden files\n";
    itsOut << "\t-a\tlist absolute paths\n";
    itsOut << "\t--ext:\"*.ext1, *.ext2,*.ext3 *.ext4\"\n\t\tcreate extension mask on listing files\n";
    itsOut << "\t--f:\"^fileName1, ^fileName2,^fileName3 ^fileName4\"\n\t\tcreate file names mask on listing files or dirs\n\t\tit understands \"*\" and \"?\" wildcards";
    itsOut << "Ctrl+C to skip\n";
}

void Parser::dirFilters()
{
    if(itsOptions.testFlag(HIDENFILES))
    {
        itsDir->setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks | QDir::Dirs | QDir::NoDotAndDotDot | QDir::AllDirs);
    }
    else
    {
        itsDir->setFilter(QDir::Files | QDir::NoSymLinks | QDir::Dirs | QDir::NoDotAndDotDot | QDir::AllDirs);
    }
    itsDir->setSorting(QDir::Name | QDir::DirsLast | QDir::Type);
}

void Parser::recursive(const QString &dirPath)
{
    itsDir->cd(dirPath);

    dirFilters();

    QFileInfoList list = itsDir->entryInfoList();

    for(int iList = 0; iList < list.size(); ++iList)
    {
        QFileInfo fileInfo = list.at(iList);
        QString filePath = fileInfo.absoluteFilePath();

        if(fileInfo.isDir())
        {
            if(itsOptions.testFlag(SHOWDIRS))
            {
                if(itsOptions.testFlag(ABSOLUTEPATH))
                {
                    itsOut << "\n" << QDir::toNativeSeparators(filePath) << ":\n";
                }
                else
                {
                    QString temp = filePath;
                    itsOut << "\nSUBDIR:\n..\\" << QDir::toNativeSeparators(temp.remove(0, QString(itsArgv[1]).size())) << ":\n";
                }
            }
            recursive(filePath);
        }
        else
        {
            itsOut << fileInfo.fileName() << "\n";
        }
    }
}

void Parser::recursive(const QString &dirPath, QTextStream &out)
{
    itsDir->cd(dirPath);

    QFileInfoList list = itsDir->entryInfoList();

    for(int iList = 0; iList < list.size(); ++iList)
    {
        QFileInfo fileInfo = list.at(iList);
        QString filePath = fileInfo.absoluteFilePath();

        if(fileInfo.isDir())
        {
            if(itsOptions.testFlag(SHOWDIRS))
            {
                if(itsOptions.testFlag(ABSOLUTEPATH))
                {
                    out << "\n" << QDir::toNativeSeparators(filePath) << ":" << "\n";
                }
                else
                {
                    QString temp = filePath;
                    out << "\nSUBDIR:\n..\\" << QDir::toNativeSeparators(temp.remove(0, QString(itsArgv[1]).size())) << ":\n";
                }
            }
            recursive(filePath, out);
        }
        else
        {
            out << fileInfo.fileName() << "\n";
        }
    }
}

void Parser::notRecursive(const QString &dirPath)
{
    itsDir->cd(dirPath);

    dirFilters();

    QFileInfoList list = itsDir->entryInfoList();

    for(int iList = 0; iList < list.size(); ++iList)
    {
        QFileInfo fileInfo = list.at(iList);
        QString filePath = fileInfo.absoluteFilePath();

        if(fileInfo.isDir())
        {
            if(itsOptions.testFlag(SHOWDIRS))
            {
                itsOut << "\n" << filePath << ":\n";
            }
        }
        else
        {
            itsOut << fileInfo.fileName() << "\n";
        }
    }
}

void Parser::notRecursive(const QString &dirPath, QTextStream &out)
{
    itsDir->cd(dirPath);

    dirFilters();

    QFileInfoList list = itsDir->entryInfoList();

    for(int iList = 0; iList < list.size(); ++iList)
    {
        QFileInfo fileInfo = list.at(iList);
        QString filePath = fileInfo.absoluteFilePath();

        if(fileInfo.isDir())
        {
            if(itsOptions.testFlag(SHOWDIRS))
            {
                out << "\n" << filePath << ":" << "\n";
            }
        }
        else
        {
            out << qPrintable(fileInfo.fileName()) << "\n";
        }
    }
}

void Parser::switcher(Parser::OPTIONS opt)
{
    if(opt.testFlag(HELP))
    {
        help();
        return;
    }
    if(!opt.testFlag(HIDECONSOLE))
    {
        parseToConsole();
    }

    // if save to file
    if(itsDir->isAbsolutePath(itsArgv[2]))
    {
        parseToFile();
    }
}

Parser::OPTIONS Parser::parseOptions()
{
    QString str;
    for(int i = 1; i < itsArgc; ++i)
    {
        str.append(QString(itsArgv[i]));
    }
#ifdef DEBUG
    qDebug() << "str:" << str;
#endif

    QVector <QStringList> opts = polyOptParser(str);

    if(opts.at(0).contains("help")) collectorOptions(HELP);
    if(opts.at(1).contains("d")) collectorOptions(SHOWDIRS);
    if(opts.at(1).contains("h")) collectorOptions(HIDECONSOLE);
    if(opts.at(1).contains("r")) collectorOptions(RECURSIVE);
    if(opts.at(1).contains("s")) collectorOptions(HIDENFILES);
    if(opts.at(1).contains("a")) collectorOptions(ABSOLUTEPATH);

    if(!opts.at(2).isEmpty())
    {
        itsFilters.append(opts.at(2));

#ifdef DEBUG
        qDebug() << "opts.at(2)" << opts.at(2);
#endif
    }
    if(!opts.at(3).isEmpty())
    {
        itsFilters.append(opts.at(3));
#ifdef DEBUG
        qDebug() << "opts.at(3)" << opts.at(3);
#endif
    }

    itsDir->setNameFilters(itsFilters);

    return getCollectedOptions();
}

void Parser::collectorOptions(Parser::OPTIONS opt)
{
    itsOptions = itsOptions | opt;
#ifdef DEBUG
    qDebug() << "itsOptions =" << QString::number(itsOptions);
#endif
}

Parser::OPTIONS Parser::getCollectedOptions() const
{
    return itsOptions;
}

QVector <QStringList> Parser::polyOptParser(QString str)
{
    QString ext = QString("(\\*\\.\\w+,?\\s?)");
    QString f = QString("(\\^\\*?\\??\\w+\\*?\\??,?\\s?)");
    QString opt_opt = QString("(-[hrdsa]+[^elp]+)");
    QString opt_help = QString("(--help)");
    QString opt_ext = QString("(--ext:\"%1+\")").arg(ext);
    QString opt_f = QString("(--f:\"%1+\")").arg(f);
    QString opt = QString("%1?\\s?%2?\\s?%3?%4?\\s?").arg(opt_help).arg(opt_opt).arg(opt_ext).arg(opt_f);

    QRegExp rx_ext(ext);
    QRegExp rx_f(f);
    QRegExp rx_opt_opt(opt_opt);
    QRegExp rx_opt_ext(opt_ext);
    QRegExp rx_opt_f(opt_f);
    QRegExp rx_opt_help(opt_help);
    QRegExp rx_opt(opt);

    QStringList listOpt;
    QStringList listExt;
    QStringList listFiles;
    QStringList listHelp;

    int pos = 0;

    QString stringExt;
    QString stringFiles;
    QString stringOpt;
    QString stringHelp;

    if((rx_opt.indexIn(str, 0) != -1) && (rx_opt_help.indexIn(str, 0) == -1))
    {
        if(rx_opt_opt.indexIn(str, 0) != -1)
        {
            while((pos = rx_opt_opt.indexIn(str, pos)) != -1)
            {
                stringOpt = rx_opt_opt.cap(1);
                listOpt.append(stringOpt);
                pos += rx_opt_opt.matchedLength();
            }
        }
        if(rx_opt_ext.indexIn(str, 0) !=-1)
        {
            pos = rx_opt_ext.indexIn(str, 0);

            while (((pos = rx_ext.indexIn(str, pos)) != -1) &&
                   ((pos < rx_opt_f.indexIn(str, 0)) || (rx_opt_f.indexIn(str, 0) == -1)))
            {
                stringExt = rx_ext.cap(1);
                listExt.append(stringExt);
                pos += rx_ext.matchedLength();
            }
        }
#ifdef DEBUG
        qDebug() << "rx_opt_f.indexIn(str, 0):" << rx_opt_f.indexIn(str, 0);
#endif
        if(rx_opt_f.indexIn(str, 0) !=-1)
        {
            pos = rx_opt_f.indexIn(str, 0);
            while ((pos = rx_f.indexIn(str, pos)) != -1)
            {
                stringFiles = rx_f.cap(1);
                listFiles.append(stringFiles);
                pos += rx_f.matchedLength();
#ifdef DEBUG
                qDebug() << "stringFiles:" << stringFiles;
#endif
            }
        }
    }
    else if((rx_opt_help.indexIn(str, 0) != -1) && (rx_opt.indexIn(str, 0) != -1))
    {
        stringHelp = rx_opt_help.cap(0);
        listHelp.append(stringHelp);
    }
    else
    {
        qErrnoWarning("Incorrect input options!");
    }

    QStringList sl;

    foreach (QString temp_str, listOpt) {
        sl.append(temp_str.replace(QRegExp("-"), "").split(QRegExp("(?=\\w)"), QString::SkipEmptyParts));
    }
    listOpt = sl;
    sl.clear();

    listOpt.removeDuplicates();

    if(listOpt.isEmpty())
        qWarning() << "\nEmpty Options\n";
    else
    {
#ifdef DEBUG
        qDebug() << "\nOptions:\n" << listOpt << "\n";
#endif
    }

    foreach (QString temp_str, listExt) {
        sl.append(temp_str.replace(QRegExp("[\\s,]"), ""));
    }
    listExt = sl;
    sl.clear();

    listExt.removeDuplicates();

    if(listExt.isEmpty())
        qWarning() << "\nEmpty Mask Extentions\n";
    else
    {
#ifdef DEBUG
        qDebug() << "\nMask Extensions:\n" << listExt << "\n";
#endif
    }

    foreach (QString temp_str, listFiles) {
        sl.append(temp_str.replace(QRegExp("[\\^\\s,]"), ""));
    }
    listFiles = sl;
    sl.clear();

    listFiles.removeDuplicates();

    if(listFiles.isEmpty())
        qWarning() << "\nEmpty Files Names\n";
    else
    {
#ifdef DEBUG
        qDebug() << "\nFiles Names:\n" << listFiles << "\n";
#endif
    }

    foreach (QString temp_str, listHelp)
    {
        sl.append(temp_str.remove(QRegExp("--")));
    }
    listHelp = sl;
    sl.clear();

    listHelp.removeDuplicates();

    if(listHelp.isEmpty())
        qWarning() << "\nEmpty Help\n";
    else
    {
#ifdef DEBUG
        qDebug() << "\nHelp:\n" << listHelp << "\n";
#endif
    }

    QVector <QStringList> list;

    list.append(listHelp); // 0
    list.append(listOpt);  // 1
    list.append(listExt);  // 2
    list.append(listFiles);// 3

#ifdef DEBUG
    qDebug() << "list.at(listHelp)" << list[0];
    qDebug() << "list.at(listOpt)" << list[1];
    qDebug() << "list.at(listExt)" << list[2];
    qDebug() << "list.at(listFiles)" << list[3];
#endif

    return list;
}
<commit_msg>Исправил проблемы с передачей параметров командной строки на кириллице.<commit_after>#include "parser.h"
#include <QFileInfoList>
#include <iostream>
#include <QString>
#include <QDebug>

Parser::Parser(int argc, char **argv)
    : itsArgc(argc)
    , itsArgv(argv)
    , itsDir(new QDir)
    , itsOptions(NONE)
    , itsFilters(QStringList() << "")
    , itsIn(stdin)
    , itsOut(stdout)
{
}

void Parser::applyOptions()
{
    parseOptions();
#ifdef DEBUG
    qDebug() << "getCollectedOptions() =" << getCollectedOptions();
    qDebug() << "(int)getCollectedOptions() =" << (int)getCollectedOptions();
#endif
    switcher(getCollectedOptions());
}

void Parser::parseToFile()
{
    dirFilters();

    if(!itsDir->exists(QString::fromLocal8Bit(itsArgv[1])))
    {
        itsOut << "EROR: path to list don't exist\n";
        return;
    }

    itsDir->cd(QString::fromLocal8Bit(itsArgv[1]));

    QFile file;
    QFileInfo info;

    file.setFileName(QString::fromLocal8Bit(itsArgv[2]));
    info.setFile(file);

    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        itsOut << "ERROR opening file:" << file.fileName();
        return;
    }

    QTextStream out(&file);
    out << "ROOT DIR:\n" << QString::fromLocal8Bit(itsArgv[1]) << ":" << "\n";

    QString filePath = info.absoluteFilePath();

    if(itsOptions.testFlag(RECURSIVE))
    {
        recursive(filePath, out);
    }
    else
    {
        notRecursive(filePath, out);
    }

    QString p = info.filePath();
    QString n = info.fileName();

    itsOut << "\nSaving file: " << info.fileName()
           << " into "
           << p.remove(p.size() - n.size(), p.size() - 1)
           << "\n";

    file.close();
}

void Parser::parseToConsole()
{

    dirFilters();

    if(!itsDir->exists(QString::fromLocal8Bit(itsArgv[1])))
    {
        itsOut << "EROR: path to list don't exist\n";
        return;
    }

    itsDir->cd(QString::fromLocal8Bit(itsArgv[1]));
    itsOut << "ROOT DIR:\n" << QString::fromLocal8Bit(itsArgv[1]) << ":\n";

    if(itsOptions.testFlag(RECURSIVE))
    {
        recursive(itsDir->absolutePath());
    }
    else
    {
        notRecursive(itsDir->absolutePath());
    }
}

void Parser::help()
{
    itsOut << "usage:\t[--help]\n";
    itsOut << "   or:\t<path to list> [options]\n";
    itsOut << "   or:\t<path to list> <path and file name to save results> [options]\n";
    itsOut << "options:\n";
    itsOut << "\t-d\tshow directories\n";
    itsOut << "\t-h\thide output to console\n";
    itsOut << "\t-r\trecursively listing\n";
    itsOut << "\t-s\tshow hidden files\n";
    itsOut << "\t-a\tlist absolute paths\n";
    itsOut << "\t--ext:\"*.ext1, *.ext2,*.ext3 *.ext4\"\n\t\tcreate extension mask on listing files\n";
    itsOut << "\t--f:\"^fileName1, ^fileName2,^fileName3 ^fileName4\"\n\t\tcreate file names mask on listing files or dirs\n\t\tit understands \"*\" and \"?\" wildcards";
    itsOut << "Ctrl+C to skip\n";
}

void Parser::dirFilters()
{
    if(itsOptions.testFlag(HIDENFILES))
    {
        itsDir->setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks | QDir::Dirs | QDir::NoDotAndDotDot | QDir::AllDirs);
    }
    else
    {
        itsDir->setFilter(QDir::Files | QDir::NoSymLinks | QDir::Dirs | QDir::NoDotAndDotDot | QDir::AllDirs);
    }
    itsDir->setSorting(QDir::Name | QDir::DirsLast | QDir::Type);
}

void Parser::recursive(const QString &dirPath)
{
    itsDir->cd(dirPath);

    dirFilters();

    QFileInfoList list = itsDir->entryInfoList();

    for(int iList = 0; iList < list.size(); ++iList)
    {
        QFileInfo fileInfo = list.at(iList);
        QString filePath = fileInfo.absoluteFilePath();

        if(fileInfo.isDir())
        {
            if(itsOptions.testFlag(SHOWDIRS))
            {
                if(itsOptions.testFlag(ABSOLUTEPATH))
                {
                    itsOut << "\n" << QDir::toNativeSeparators(filePath) << ":\n";
                }
                else
                {
                    QString temp = filePath;
                    itsOut << "\nSUBDIR:\n.." << QDir::toNativeSeparators(temp.remove(0, QString(itsArgv[1]).size())) << ":\n";
                }
            }
            recursive(filePath);
        }
        else
        {
            itsOut << fileInfo.fileName() << "\n";
        }
    }
}

void Parser::recursive(const QString &dirPath, QTextStream &out)
{
    itsDir->cd(dirPath);

    QFileInfoList list = itsDir->entryInfoList();

    for(int iList = 0; iList < list.size(); ++iList)
    {
        QFileInfo fileInfo = list.at(iList);
        QString filePath = fileInfo.absoluteFilePath();

        if(fileInfo.isDir())
        {
            if(itsOptions.testFlag(SHOWDIRS))
            {
                if(itsOptions.testFlag(ABSOLUTEPATH))
                {
                    out << "\n" << QDir::toNativeSeparators(filePath) << ":" << "\n";
                }
                else
                {
                    QString temp = filePath;
                    out << "\nSUBDIR:\n.." << QDir::toNativeSeparators(temp.remove(0, QString(itsArgv[1]).size())) << ":\n";
                }
            }
            recursive(filePath, out);
        }
        else
        {
            out << fileInfo.fileName() << "\n";
        }
    }
}

void Parser::notRecursive(const QString &dirPath)
{
    itsDir->cd(dirPath);

    dirFilters();

    QFileInfoList list = itsDir->entryInfoList();

    for(int iList = 0; iList < list.size(); ++iList)
    {
        QFileInfo fileInfo = list.at(iList);
        QString filePath = fileInfo.absoluteFilePath();

        if(fileInfo.isDir())
        {
            if(itsOptions.testFlag(SHOWDIRS))
            {
                itsOut << "\n" << filePath << ":\n";
            }
        }
        else
        {
            itsOut << fileInfo.fileName() << "\n";
        }
    }
}

void Parser::notRecursive(const QString &dirPath, QTextStream &out)
{
    itsDir->cd(dirPath);

    dirFilters();

    QFileInfoList list = itsDir->entryInfoList();

    for(int iList = 0; iList < list.size(); ++iList)
    {
        QFileInfo fileInfo = list.at(iList);
        QString filePath = fileInfo.absoluteFilePath();

        if(fileInfo.isDir())
        {
            if(itsOptions.testFlag(SHOWDIRS))
            {
                out << "\n" << filePath << ":" << "\n";
            }
        }
        else
        {
            out << fileInfo.fileName() << "\n";
        }
    }
}

void Parser::switcher(Parser::OPTIONS opt)
{
    if(opt.testFlag(HELP))
    {
        help();
        return;
    }
    if(!opt.testFlag(HIDECONSOLE))
    {
        parseToConsole();
    }

    // if save to file
    if(itsDir->isAbsolutePath(itsArgv[2]))
    {
        parseToFile();
    }
}

Parser::OPTIONS Parser::parseOptions()
{
    QString str;
    for(int i = 1; i < itsArgc; ++i)
    {
        str.append(QString::fromLocal8Bit(itsArgv[i]));
    }
#ifdef DEBUG
    qDebug() << "str:" << str;
#endif

    QVector <QStringList> opts = polyOptParser(str);

    if(opts.at(0).contains("help")) collectorOptions(HELP);
    if(opts.at(1).contains("d")) collectorOptions(SHOWDIRS);
    if(opts.at(1).contains("h")) collectorOptions(HIDECONSOLE);
    if(opts.at(1).contains("r")) collectorOptions(RECURSIVE);
    if(opts.at(1).contains("s")) collectorOptions(HIDENFILES);
    if(opts.at(1).contains("a")) collectorOptions(ABSOLUTEPATH);

    if(!opts.at(2).isEmpty())
    {
        itsFilters.append(opts.at(2));

#ifdef DEBUG
        qDebug() << "opts.at(2)" << opts.at(2);
#endif
    }
    if(!opts.at(3).isEmpty())
    {
        itsFilters.append(opts.at(3));
#ifdef DEBUG
        qDebug() << "opts.at(3)" << opts.at(3);
#endif
    }

    itsDir->setNameFilters(itsFilters);

    return getCollectedOptions();
}

void Parser::collectorOptions(Parser::OPTIONS opt)
{
    itsOptions = itsOptions | opt;
#ifdef DEBUG
    qDebug() << "itsOptions =" << QString::number(itsOptions);
#endif
}

Parser::OPTIONS Parser::getCollectedOptions() const
{
    return itsOptions;
}

QVector <QStringList> Parser::polyOptParser(QString str)
{
    QString ext = QString("(\\*\\.\\w+,?\\s?)");
    QString f = QString("(\\^\\*?\\??\\w+\\*?\\??,?\\s?)");
    QString opt_opt = QString("(-[hrdsa]+[^elp]+)");
    QString opt_help = QString("(--help)");
    QString opt_ext = QString("(--ext:\"%1+\")").arg(ext);
    QString opt_f = QString("(--f:\"%1+\")").arg(f);
    QString opt = QString("%1?\\s?%2?\\s?%3?%4?\\s?").arg(opt_help).arg(opt_opt).arg(opt_ext).arg(opt_f);

    QRegExp rx_ext(ext);
    QRegExp rx_f(f);
    QRegExp rx_opt_opt(opt_opt);
    QRegExp rx_opt_ext(opt_ext);
    QRegExp rx_opt_f(opt_f);
    QRegExp rx_opt_help(opt_help);
    QRegExp rx_opt(opt);

    QStringList listOpt;
    QStringList listExt;
    QStringList listFiles;
    QStringList listHelp;

    int pos = 0;

    QString stringExt;
    QString stringFiles;
    QString stringOpt;
    QString stringHelp;

    if((rx_opt.indexIn(str, 0) != -1) && (rx_opt_help.indexIn(str, 0) == -1))
    {
        if(rx_opt_opt.indexIn(str, 0) != -1)
        {
            while((pos = rx_opt_opt.indexIn(str, pos)) != -1)
            {
                stringOpt = rx_opt_opt.cap(1);
                listOpt.append(stringOpt);
                pos += rx_opt_opt.matchedLength();
            }
        }
        if(rx_opt_ext.indexIn(str, 0) !=-1)
        {
            pos = rx_opt_ext.indexIn(str, 0);

            while (((pos = rx_ext.indexIn(str, pos)) != -1) &&
                   ((pos < rx_opt_f.indexIn(str, 0)) || (rx_opt_f.indexIn(str, 0) == -1)))
            {
                stringExt = rx_ext.cap(1);
                listExt.append(stringExt);
                pos += rx_ext.matchedLength();
            }
        }
#ifdef DEBUG
        qDebug() << "rx_opt_f.indexIn(str, 0):" << rx_opt_f.indexIn(str, 0);
#endif
        if(rx_opt_f.indexIn(str, 0) !=-1)
        {
            pos = rx_opt_f.indexIn(str, 0);
            while ((pos = rx_f.indexIn(str, pos)) != -1)
            {
                stringFiles = rx_f.cap(1);
                listFiles.append(stringFiles);
                pos += rx_f.matchedLength();
#ifdef DEBUG
                qDebug() << "stringFiles:" << stringFiles;
#endif
            }
        }
    }
    else if((rx_opt_help.indexIn(str, 0) != -1) && (rx_opt.indexIn(str, 0) != -1))
    {
        stringHelp = rx_opt_help.cap(0);
        listHelp.append(stringHelp);
    }
    else
    {
        qErrnoWarning("Incorrect input options!");
    }

    QStringList sl;

    foreach (QString temp_str, listOpt) {
        sl.append(temp_str.replace(QRegExp("-"), "").split(QRegExp("(?=\\w)"), QString::SkipEmptyParts));
    }
    listOpt = sl;
    sl.clear();

    listOpt.removeDuplicates();

    if(listOpt.isEmpty())
        qWarning() << "\nEmpty Options\n";
    else
    {
#ifdef DEBUG
        qDebug() << "\nOptions:\n" << listOpt << "\n";
#endif
    }

    foreach (QString temp_str, listExt) {
        sl.append(temp_str.replace(QRegExp("[\\s,]"), ""));
    }
    listExt = sl;
    sl.clear();

    listExt.removeDuplicates();

    if(listExt.isEmpty())
        qWarning() << "\nEmpty Mask Extentions\n";
    else
    {
#ifdef DEBUG
        qDebug() << "\nMask Extensions:\n" << listExt << "\n";
#endif
    }

    foreach (QString temp_str, listFiles) {
        sl.append(temp_str.replace(QRegExp("[\\^\\s,]"), ""));
    }
    listFiles = sl;
    sl.clear();

    listFiles.removeDuplicates();

    if(listFiles.isEmpty())
        qWarning() << "\nEmpty Files Names\n";
    else
    {
#ifdef DEBUG
        qDebug() << "\nFiles Names:\n" << listFiles << "\n";
#endif
    }

    foreach (QString temp_str, listHelp)
    {
        sl.append(temp_str.remove(QRegExp("--")));
    }
    listHelp = sl;
    sl.clear();

    listHelp.removeDuplicates();

    if(listHelp.isEmpty())
        qWarning() << "\nEmpty Help\n";
    else
    {
#ifdef DEBUG
        qDebug() << "\nHelp:\n" << listHelp << "\n";
#endif
    }

    QVector <QStringList> list;

    list.append(listHelp); // 0
    list.append(listOpt);  // 1
    list.append(listExt);  // 2
    list.append(listFiles);// 3

#ifdef DEBUG
    qDebug() << "list.at(listHelp)" << list[0];
    qDebug() << "list.at(listOpt)" << list[1];
    qDebug() << "list.at(listExt)" << list[2];
    qDebug() << "list.at(listFiles)" << list[3];
#endif

    return list;
}
<|endoftext|>


<commit_before>/***************************************************************************
						  constraint_basedialog.cpp  -  description
							 -------------------
	begin                : 2017
	copyright            : (C) 2017 by Rodolfo RG
	This file is part of a modification of FET timetable (the original is developed by Liviu Lalescu)
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software: you can redistribute it and/or modify  *
 *   it under the terms of the GNU Affero General Public License as        *
 *   published by the Free Software Foundation, either version 3 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 ***************************************************************************/

#include "constraint_basedialog.h"

#include <QMessageBox>
#include <QScrollBar>
#include <QShortcut>

#include <cassert>

#include "longtextmessagebox.h"

#include "centerwidgetonscreen.h"

ConstraintBaseDialog::ConstraintBaseDialog(QWidget* parent): QDialog(parent),
	  filterWidget(nullptr)
{
	setupUi(this);

	currentConstraintTextEdit->setReadOnly(true);

	modifyConstraintPushButton->setDefault(true);

	helpPushButton->setVisible(false);

	instructionsLabel->setVisible(false);

	constraintsListWidget->setSelectionMode(QAbstractItemView::SingleSelection);

	connect(constraintsListWidget, SIGNAL(currentRowChanged(int)), this, SLOT(constraintChanged(int)));
	connect(addConstraintPushButton, SIGNAL(clicked()), this, SLOT(addConstraint()));
	connect(closePushButton, SIGNAL(clicked()), this, SLOT(close()));
	connect(removeConstraintPushButton, SIGNAL(clicked()), this, SLOT(removeConstraint()));
	connect(modifyConstraintPushButton, SIGNAL(clicked()), this, SLOT(modifyConstraint()));
	connect(commentsPushButton, SIGNAL(clicked()), this, SLOT(editComments()));
	connect(activeCheckBox, SIGNAL(clicked(bool)), this, SLOT(toggleActiveConstraint(bool)));
	connect(constraintsListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(modifyConstraint()));
	connect(helpPushButton, SIGNAL(clicked()), this, SLOT(help()));

	QShortcut* shortcut = new QShortcut(QKeySequence(Qt::Key_Delete), constraintsListWidget);
	connect(shortcut, SIGNAL(activated()), this, SLOT(deleteItem()));

	centerWidgetOnScreen(this);

	modifyConstraintPushButton->setEnabled(false);
	removeConstraintPushButton->setEnabled(false);
	commentsPushButton->setEnabled(false);
	activeCheckBox->setEnabled(false);
//	populateFilters();
//	filterChanged();
}

ConstraintBaseDialog::~ConstraintBaseDialog()
{
	delete filterWidget;
}

void ConstraintBaseDialog::setFilterWidget(QWidget *widget)
{
	if (filterWidget != nullptr) {
		rightVerticalLayout->removeWidget(filterWidget);
	}
	filterWidget = widget;
	if (widget != nullptr) {
		rightVerticalLayout->addWidget(widget);
	}
}

void ConstraintBaseDialog::filterChanged()
{
	// The clear order matters.
	// When clearing QListWidget, it emits currentRowChanged signal that
	// with index 0 and QList size 0 also if in the inverse clearing order.
	// Better solution: use model-view concept instead of this approach.
	// Or may just use QObject::blockSignals(bool)
	constraintsListWidget->clear();
	visibleConstraintsList.clear();

	fillConstraintList(visibleConstraintsList);

	if(constraintsListWidget->count()>0)
		constraintsListWidget->setCurrentRow(0);
	else
		this->constraintChanged(-1);
}

void ConstraintBaseDialog::constraintChanged(int index)
{
	if(index<0){
		currentConstraintTextEdit->setPlainText("");
		modifyConstraintPushButton->setEnabled(false);
		removeConstraintPushButton->setEnabled(false);
		commentsPushButton->setEnabled(false);
		activeCheckBox->setEnabled(false);
		activeCheckBox->setChecked(false);
		return;
	}

	assert(index<this->visibleConstraintsList.size());
	QString s=getConstraintDetailedDescription(visibleConstraintsList.at(index));
	currentConstraintTextEdit->setPlainText(s);
	modifyConstraintPushButton->setEnabled(true);
	removeConstraintPushButton->setEnabled(true);
	commentsPushButton->setEnabled(true);
	activeCheckBox->setEnabled(true);
	activeCheckBox->setChecked(isConstraintActive(visibleConstraintsList.at(index)));
}

void ConstraintBaseDialog::addConstraint()
{
	QDialog *form = createAddDialog();
//	setParentAndOtherThings(form, this);
	form->exec();
	delete form;

	filterChanged();

	constraintsListWidget->setCurrentRow(constraintsListWidget->count()-1);
}

void ConstraintBaseDialog::modifyConstraint()
{
	int valv=constraintsListWidget->verticalScrollBar()->value();
	int valh=constraintsListWidget->horizontalScrollBar()->value();

	int i=constraintsListWidget->currentRow();
	if(i<0){
		QMessageBox::information(this, tr("FET information"), tr("Invalid selected constraint"));
		return;
	}
	void* ctr=this->visibleConstraintsList.at(i);

	QDialog *form = createModifyDialog(ctr);
//	setParentAndOtherThings(form, this);
	form->exec();
	delete form;

	filterChanged();

	constraintsListWidget->verticalScrollBar()->setValue(valv);
	constraintsListWidget->horizontalScrollBar()->setValue(valh);

	if(i>=constraintsListWidget->count())
		i=constraintsListWidget->count()-1;

	if(i>=0)
		constraintsListWidget->setCurrentRow(i);
	else
		this->constraintChanged(-1);
}

void ConstraintBaseDialog::removeConstraint()
{
	int i=constraintsListWidget->currentRow();
	if(i<0){
		QMessageBox::information(this, tr("FET information"), tr("Invalid selected constraint"));
		return;
	}
	void* ctr=this->visibleConstraintsList.at(i);
	QString s;
	s=tr("Remove constraint?");
	s+="\n\n";
	s+=getConstraintDetailedDescription(ctr);

	QListWidgetItem* item;

	switch( LongTextMessageBox::confirmation( this, tr("FET confirmation"),
		s, tr("&Yes"), tr("&No"), 0, 0, 1 ) ){
	case 0: // The user clicked the OK button or pressed Enter
		if (!beforeRemoveConstraint())
			break;

		doRemoveConstraint(ctr);

		visibleConstraintsList.removeAt(i);
		constraintsListWidget->setCurrentRow(-1);
		item=constraintsListWidget->takeItem(i);
		delete item;

		afterRemoveConstraint();

		break;
	case 1: // The user clicked the Cancel button or pressed Escape
		break;
	}

	if(i>=constraintsListWidget->count())
		i=constraintsListWidget->count()-1;
	if(i>=0)
		constraintsListWidget->setCurrentRow(i);
	else
		this->constraintChanged(-1);
}

void ConstraintBaseDialog::editComments()
{
	int i=constraintsListWidget->currentRow();
	if(i<0){
		QMessageBox::information(this, tr("FET information"), tr("Invalid selected constraint"));
		return;
	}

	void* ctr=this->visibleConstraintsList.at(i);
	editComments(ctr);

	filterChanged();
	constraintsListWidget->setCurrentRow(i);
}

void ConstraintBaseDialog::toggleActiveConstraint(bool checked)
{
	int i=constraintsListWidget->currentRow();
	if(i<0){
		QMessageBox::information(this, tr("FET information"), tr("Invalid selected constraint"));
		return;
	}

	void* ctr=this->visibleConstraintsList.at(i);
	toggleActiveConstraint(ctr, checked);

	filterChanged();
	constraintsListWidget->setCurrentRow(i);
}

void ConstraintBaseDialog::deleteItem()
{
	QListWidgetItem * item = constraintsListWidget->currentItem();

	if (item)
		removeConstraint();
}

bool ConstraintBaseDialog::beforeRemoveConstraint()
{
	return true;
}

void ConstraintBaseDialog::afterRemoveConstraint()
{
}

void ConstraintBaseDialog::setHelpText(QString msg)
{
	helpPushButton->setHidden(msg.isEmpty());
	helpMsg = msg;
}

void ConstraintBaseDialog::help()
{
	LongTextMessageBox::largeInformation(this, tr("FET help"), helpMsg);
}

QWidget *ConstraintBaseDialog::getFilterWidget() const
{
	return filterWidget;
}

void ConstraintBaseDialog::setInstructionText(QString msg)
{
	instructionsLabel->setHidden(msg.isEmpty());
	instructionsLabel->setText(msg);
}
<commit_msg>Inactive constraints are listed in different background color<commit_after>/***************************************************************************
						  constraint_basedialog.cpp  -  description
							 -------------------
	begin                : 2017
	copyright            : (C) 2017 by Rodolfo RG
	This file is part of a modification of FET timetable (the original is developed by Liviu Lalescu)
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software: you can redistribute it and/or modify  *
 *   it under the terms of the GNU Affero General Public License as        *
 *   published by the Free Software Foundation, either version 3 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 ***************************************************************************/

#include "constraint_basedialog.h"

#include <QMessageBox>
#include <QScrollBar>
#include <QShortcut>

#include <cassert>

#include "longtextmessagebox.h"

#include "centerwidgetonscreen.h"
#include "timetable_defs.h"

ConstraintBaseDialog::ConstraintBaseDialog(QWidget* parent): QDialog(parent),
	  filterWidget(nullptr)
{
	setupUi(this);

	currentConstraintTextEdit->setReadOnly(true);

	modifyConstraintPushButton->setDefault(true);

	helpPushButton->setVisible(false);

	instructionsLabel->setVisible(false);

	constraintsListWidget->setSelectionMode(QAbstractItemView::SingleSelection);

	connect(constraintsListWidget, SIGNAL(currentRowChanged(int)), this, SLOT(constraintChanged(int)));
	connect(addConstraintPushButton, SIGNAL(clicked()), this, SLOT(addConstraint()));
	connect(closePushButton, SIGNAL(clicked()), this, SLOT(close()));
	connect(removeConstraintPushButton, SIGNAL(clicked()), this, SLOT(removeConstraint()));
	connect(modifyConstraintPushButton, SIGNAL(clicked()), this, SLOT(modifyConstraint()));
	connect(commentsPushButton, SIGNAL(clicked()), this, SLOT(editComments()));
	connect(activeCheckBox, SIGNAL(clicked(bool)), this, SLOT(toggleActiveConstraint(bool)));
	connect(constraintsListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(modifyConstraint()));
	connect(helpPushButton, SIGNAL(clicked()), this, SLOT(help()));

	QShortcut* shortcut = new QShortcut(QKeySequence(Qt::Key_Delete), constraintsListWidget);
	connect(shortcut, SIGNAL(activated()), this, SLOT(deleteItem()));

	centerWidgetOnScreen(this);

	modifyConstraintPushButton->setEnabled(false);
	removeConstraintPushButton->setEnabled(false);
	commentsPushButton->setEnabled(false);
	activeCheckBox->setEnabled(false);
//	populateFilters();
//	filterChanged();
}

ConstraintBaseDialog::~ConstraintBaseDialog()
{
	delete filterWidget;
}

void ConstraintBaseDialog::setFilterWidget(QWidget *widget)
{
	if (filterWidget != nullptr) {
		rightVerticalLayout->removeWidget(filterWidget);
	}
	filterWidget = widget;
	if (widget != nullptr) {
		rightVerticalLayout->addWidget(widget);
	}
}

void ConstraintBaseDialog::filterChanged()
{
	// The clear order matters.
	// When clearing QListWidget, it emits currentRowChanged signal that
	// with index 0 and QList size 0 also if in the inverse clearing order.
	// Better solution: use model-view concept instead of this approach.
	// Or may just use QObject::blockSignals(bool)
	constraintsListWidget->clear();
	visibleConstraintsList.clear();

	fillConstraintList(visibleConstraintsList);

	if(USE_GUI_COLORS) {
		for(int i = 0; i < constraintsListWidget->count(); ++i)
		{
			QListWidgetItem* item = constraintsListWidget->item(i);
			if (isConstraintActive(visibleConstraintsList[i]))
				item->setBackground(constraintsListWidget->palette().base());
			else
				item->setBackground(constraintsListWidget->palette().alternateBase());
		}
	}

	if(constraintsListWidget->count()>0)
		constraintsListWidget->setCurrentRow(0);
	else
		this->constraintChanged(-1);
}

void ConstraintBaseDialog::constraintChanged(int index)
{
	if(index<0){
		currentConstraintTextEdit->setPlainText("");
		modifyConstraintPushButton->setEnabled(false);
		removeConstraintPushButton->setEnabled(false);
		commentsPushButton->setEnabled(false);
		activeCheckBox->setEnabled(false);
		activeCheckBox->setChecked(false);
		return;
	}

	assert(index<this->visibleConstraintsList.size());
	QString s=getConstraintDetailedDescription(visibleConstraintsList.at(index));
	currentConstraintTextEdit->setPlainText(s);
	modifyConstraintPushButton->setEnabled(true);
	removeConstraintPushButton->setEnabled(true);
	commentsPushButton->setEnabled(true);
	activeCheckBox->setEnabled(true);
	activeCheckBox->setChecked(isConstraintActive(visibleConstraintsList.at(index)));
}

void ConstraintBaseDialog::addConstraint()
{
	QDialog *form = createAddDialog();
//	setParentAndOtherThings(form, this);
	form->exec();
	delete form;

	filterChanged();

	constraintsListWidget->setCurrentRow(constraintsListWidget->count()-1);
}

void ConstraintBaseDialog::modifyConstraint()
{
	int valv=constraintsListWidget->verticalScrollBar()->value();
	int valh=constraintsListWidget->horizontalScrollBar()->value();

	int i=constraintsListWidget->currentRow();
	if(i<0){
		QMessageBox::information(this, tr("FET information"), tr("Invalid selected constraint"));
		return;
	}
	void* ctr=this->visibleConstraintsList.at(i);

	QDialog *form = createModifyDialog(ctr);
//	setParentAndOtherThings(form, this);
	form->exec();
	delete form;

	filterChanged();

	constraintsListWidget->verticalScrollBar()->setValue(valv);
	constraintsListWidget->horizontalScrollBar()->setValue(valh);

	if(i>=constraintsListWidget->count())
		i=constraintsListWidget->count()-1;

	if(i>=0)
		constraintsListWidget->setCurrentRow(i);
	else
		this->constraintChanged(-1);
}

void ConstraintBaseDialog::removeConstraint()
{
	int i=constraintsListWidget->currentRow();
	if(i<0){
		QMessageBox::information(this, tr("FET information"), tr("Invalid selected constraint"));
		return;
	}
	void* ctr=this->visibleConstraintsList.at(i);
	QString s;
	s=tr("Remove constraint?");
	s+="\n\n";
	s+=getConstraintDetailedDescription(ctr);

	QListWidgetItem* item;

	switch( LongTextMessageBox::confirmation( this, tr("FET confirmation"),
		s, tr("&Yes"), tr("&No"), 0, 0, 1 ) ){
	case 0: // The user clicked the OK button or pressed Enter
		if (!beforeRemoveConstraint())
			break;

		doRemoveConstraint(ctr);

		visibleConstraintsList.removeAt(i);
		constraintsListWidget->setCurrentRow(-1);
		item=constraintsListWidget->takeItem(i);
		delete item;

		afterRemoveConstraint();

		break;
	case 1: // The user clicked the Cancel button or pressed Escape
		break;
	}

	if(i>=constraintsListWidget->count())
		i=constraintsListWidget->count()-1;
	if(i>=0)
		constraintsListWidget->setCurrentRow(i);
	else
		this->constraintChanged(-1);
}

void ConstraintBaseDialog::editComments()
{
	int i=constraintsListWidget->currentRow();
	if(i<0){
		QMessageBox::information(this, tr("FET information"), tr("Invalid selected constraint"));
		return;
	}

	void* ctr=this->visibleConstraintsList.at(i);
	editComments(ctr);

	filterChanged();
	constraintsListWidget->setCurrentRow(i);
}

void ConstraintBaseDialog::toggleActiveConstraint(bool checked)
{
	int i=constraintsListWidget->currentRow();
	if(i<0){
		QMessageBox::information(this, tr("FET information"), tr("Invalid selected constraint"));
		return;
	}

	void* ctr=this->visibleConstraintsList.at(i);
	toggleActiveConstraint(ctr, checked);

	filterChanged();
	constraintsListWidget->setCurrentRow(i);
}

void ConstraintBaseDialog::deleteItem()
{
	QListWidgetItem * item = constraintsListWidget->currentItem();

	if (item)
		removeConstraint();
}

bool ConstraintBaseDialog::beforeRemoveConstraint()
{
	return true;
}

void ConstraintBaseDialog::afterRemoveConstraint()
{
}

void ConstraintBaseDialog::setHelpText(QString msg)
{
	helpPushButton->setHidden(msg.isEmpty());
	helpMsg = msg;
}

void ConstraintBaseDialog::help()
{
	LongTextMessageBox::largeInformation(this, tr("FET help"), helpMsg);
}

QWidget *ConstraintBaseDialog::getFilterWidget() const
{
	return filterWidget;
}

void ConstraintBaseDialog::setInstructionText(QString msg)
{
	instructionsLabel->setHidden(msg.isEmpty());
	instructionsLabel->setText(msg);
}
<|endoftext|>


<commit_before>/** \file
 *
 *  Copyright (c) 2015 by Travis Gockel. All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify it under the terms of the Apache License
 *  as published by the Apache Software Foundation, either version 2 of the License, or (at your option) any later
 *  version.
 *
 *  \author Travis Gockel (travis@gockelhut.com)
**/
#include "test.hpp"

#include <jsonv/parse.hpp>
#include <jsonv/serialization.hpp>
#include <jsonv/value.hpp>
#include <jsonv/detail/scope_exit.hpp>

#include <cstdint>
#include <string>
#include <tuple>
#include <typeinfo>
#include <typeindex>
#include <utility>

namespace jsonv_test
{

using namespace jsonv;

namespace
{

struct unassociated { };

struct my_thing
{
    int a;
    int b;
    std::string c;

    my_thing(const value& from, const extraction_context& cxt) :
            a(cxt.extract_sub<int>(from, "a")),
            b(cxt.extract_sub<int>(from, "b")),
            c(cxt.extract_sub<std::string>(from, "c"))
    { }

    my_thing(int a, int b, std::string c) :
            a(a),
            b(b),
            c(std::move(c))
    { }

    static const extractor* get_extractor()
    {
        static extractor_construction<my_thing> instance;
        return &instance;
    }

    bool operator==(const my_thing& other) const
    {
        return std::tie(a, b, c) == std::tie(other.a, other.b, other.c);
    }

    friend std::ostream& operator<<(std::ostream& os, const my_thing& self)
    {
        return os << "{ a=" << self.a << ", b=" << self.b << ", c=" << self.c << " }";
    }

    friend std::string to_string(const my_thing& self)
    {
        std::ostringstream os;
        os << self;
        return os.str();
    }
};

}

TEST(extract_basics)
{
    value val = parse(R"({
                        "i": 5,
                        "d": 4.5,
                        "s": "thing",
                        "a": [ 1, 2, 3 ],
                        "o": { "i": 5, "d": 4.5 }
                      })");
    extraction_context cxt(formats::defaults());
    ensure_eq(val, cxt.extract<value>(val));
    ensure_eq(5, cxt.extract_sub<std::int8_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::uint8_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::int16_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::uint16_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::int32_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::uint32_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::int64_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::uint64_t>(val, "i"));
    ensure_eq(4.5f, cxt.extract_sub<float>(val, "d"));
    ensure_eq(4.5, cxt.extract_sub<double>(val, "d"));
    ensure_eq("thing", cxt.extract_sub<std::string>(val, "s"));
    try
    {
        cxt.extract_sub<unassociated>(val, "o");
    }
    catch (const extraction_error& extract_err)
    {
        ensure_eq(path::create(".o"), extract_err.path());
        ensure(extract_err.nested_ptr());

        try
        {
            std::rethrow_exception(extract_err.nested_ptr());
        }
        catch (const no_extractor& noex)
        {
            ensure_eq(std::string(typeid(unassociated).name()), noex.type_name());
            ensure(noex.type_index() == std::type_index(typeid(unassociated)));
        }
    }

    try
    {
        cxt.extract_sub<int>(val, path::create(".a[3]"));
    }
    catch (const extraction_error& extract_err)
    {
        ensure_eq(path::create(".a[3]"), extract_err.path());
    }
}

TEST(extract_object)
{
    formats fmts = formats::compose({ formats::defaults() });
    fmts.register_extractor(my_thing::get_extractor());

    my_thing res = extract<my_thing>(parse(R"({ "a": 1, "b": 2, "c": "thing" })"), fmts);
    to_string(res);
    ensure_eq(my_thing(1, 2, "thing"), res);
}

TEST(extract_object_with_unique_extractor)
{
    formats fmts = formats::compose({ formats::defaults() });
    fmts.register_extractor(std::unique_ptr<extractor>(new extractor_construction<my_thing>()));

    my_thing res = extract<my_thing>(parse(R"({ "a": 1, "b": 2, "c": "thing" })"), fmts);
    ensure_eq(my_thing(1, 2, "thing"), res);
}

TEST(extract_object_search)
{
    formats base_fmts;
    base_fmts.register_extractor(my_thing::get_extractor());
    formats fmts = formats::compose({ formats::defaults(), base_fmts });

    my_thing res = extract<my_thing>(parse(R"({ "a": 1, "b": 2, "c": "thing" })"), fmts);
    ensure_eq(my_thing(1, 2, "thing"), res);
}

TEST(extract_object_with_globals)
{
    {
        formats base_fmts;
        base_fmts.register_extractor(my_thing::get_extractor());
        formats::set_global(formats::compose({ formats::defaults(), base_fmts }));
    }
    auto reset_global_on_exit = jsonv::detail::on_scope_exit([] { formats::reset_global(); });

    my_thing res = extract<my_thing>(parse(R"({ "a": 1, "b": 2, "c": "thing" })"));
    ensure_eq(my_thing(1, 2, "thing"), res);
}

TEST(extract_coerce)
{
    value val = parse(R"({
                        "i": 5,
                        "d": 4.5,
                        "s": "10",
                        "a": [ 1, 2, 3 ],
                        "o": { "i": 5, "d": 4.5 }
                      })");
    extraction_context cxt(formats::coerce());

    // regular
    ensure_eq(val, cxt.extract<value>(val));
    ensure_eq(5, cxt.extract_sub<std::int8_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::uint8_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::int16_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::uint16_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::int32_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::uint32_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::int64_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::uint64_t>(val, "i"));
    ensure_eq(4.5f, cxt.extract_sub<float>(val, "d"));
    ensure_eq(4.5, cxt.extract_sub<double>(val, "d"));
    ensure_eq("10", cxt.extract_sub<std::string>(val, "s"));

    // some coercing...
    ensure_eq("5", cxt.extract_sub<std::string>(val, "i"));
    ensure_eq(10, cxt.extract_sub<int>(val, "s"));
}

}
<commit_msg>Adds unit test to check that extraction_context always throws an extraction_error.<commit_after>/** \file
 *
 *  Copyright (c) 2015 by Travis Gockel. All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify it under the terms of the Apache License
 *  as published by the Apache Software Foundation, either version 2 of the License, or (at your option) any later
 *  version.
 *
 *  \author Travis Gockel (travis@gockelhut.com)
**/
#include "test.hpp"

#include <jsonv/parse.hpp>
#include <jsonv/serialization.hpp>
#include <jsonv/value.hpp>
#include <jsonv/detail/scope_exit.hpp>

#include <cstdint>
#include <string>
#include <tuple>
#include <typeinfo>
#include <typeindex>
#include <utility>

namespace jsonv_test
{

using namespace jsonv;

namespace
{

struct unassociated { };

struct my_thing
{
    int a;
    int b;
    std::string c;

    my_thing(const value& from, const extraction_context& cxt) :
            a(cxt.extract_sub<int>(from, "a")),
            b(cxt.extract_sub<int>(from, "b")),
            c(cxt.extract_sub<std::string>(from, "c"))
    { }

    my_thing(int a, int b, std::string c) :
            a(a),
            b(b),
            c(std::move(c))
    { }

    static const extractor* get_extractor()
    {
        static extractor_construction<my_thing> instance;
        return &instance;
    }

    bool operator==(const my_thing& other) const
    {
        return std::tie(a, b, c) == std::tie(other.a, other.b, other.c);
    }

    friend std::ostream& operator<<(std::ostream& os, const my_thing& self)
    {
        return os << "{ a=" << self.a << ", b=" << self.b << ", c=" << self.c << " }";
    }

    friend std::string to_string(const my_thing& self)
    {
        std::ostringstream os;
        os << self;
        return os.str();
    }
};

}

TEST(extract_basics)
{
    value val = parse(R"({
                        "i": 5,
                        "d": 4.5,
                        "s": "thing",
                        "a": [ 1, 2, 3 ],
                        "o": { "i": 5, "d": 4.5 }
                      })");
    extraction_context cxt(formats::defaults());
    ensure_eq(val, cxt.extract<value>(val));
    ensure_eq(5, cxt.extract_sub<std::int8_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::uint8_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::int16_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::uint16_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::int32_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::uint32_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::int64_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::uint64_t>(val, "i"));
    ensure_eq(4.5f, cxt.extract_sub<float>(val, "d"));
    ensure_eq(4.5, cxt.extract_sub<double>(val, "d"));
    ensure_eq("thing", cxt.extract_sub<std::string>(val, "s"));
    try
    {
        cxt.extract_sub<unassociated>(val, "o");
    }
    catch (const extraction_error& extract_err)
    {
        ensure_eq(path::create(".o"), extract_err.path());
        ensure(extract_err.nested_ptr());

        try
        {
            std::rethrow_exception(extract_err.nested_ptr());
        }
        catch (const no_extractor& noex)
        {
            ensure_eq(std::string(typeid(unassociated).name()), noex.type_name());
            ensure(noex.type_index() == std::type_index(typeid(unassociated)));
        }
    }

    try
    {
        cxt.extract_sub<int>(val, path::create(".a[3]"));
    }
    catch (const extraction_error& extract_err)
    {
        ensure_eq(path::create(".a[3]"), extract_err.path());
    }
}

TEST(extract_object)
{
    formats fmts = formats::compose({ formats::defaults() });
    fmts.register_extractor(my_thing::get_extractor());

    my_thing res = extract<my_thing>(parse(R"({ "a": 1, "b": 2, "c": "thing" })"), fmts);
    to_string(res);
    ensure_eq(my_thing(1, 2, "thing"), res);
}

TEST(extract_object_with_unique_extractor)
{
    formats fmts = formats::compose({ formats::defaults() });
    fmts.register_extractor(std::unique_ptr<extractor>(new extractor_construction<my_thing>()));

    my_thing res = extract<my_thing>(parse(R"({ "a": 1, "b": 2, "c": "thing" })"), fmts);
    ensure_eq(my_thing(1, 2, "thing"), res);
}

TEST(extract_object_search)
{
    formats base_fmts;
    base_fmts.register_extractor(my_thing::get_extractor());
    formats fmts = formats::compose({ formats::defaults(), base_fmts });

    my_thing res = extract<my_thing>(parse(R"({ "a": 1, "b": 2, "c": "thing" })"), fmts);
    ensure_eq(my_thing(1, 2, "thing"), res);
}

TEST(extract_object_with_globals)
{
    {
        formats base_fmts;
        base_fmts.register_extractor(my_thing::get_extractor());
        formats::set_global(formats::compose({ formats::defaults(), base_fmts }));
    }
    auto reset_global_on_exit = jsonv::detail::on_scope_exit([] { formats::reset_global(); });

    my_thing res = extract<my_thing>(parse(R"({ "a": 1, "b": 2, "c": "thing" })"));
    ensure_eq(my_thing(1, 2, "thing"), res);
}

TEST(extract_coerce)
{
    value val = parse(R"({
                        "i": 5,
                        "d": 4.5,
                        "s": "10",
                        "a": [ 1, 2, 3 ],
                        "o": { "i": 5, "d": 4.5 }
                      })");
    extraction_context cxt(formats::coerce());

    // regular
    ensure_eq(val, cxt.extract<value>(val));
    ensure_eq(5, cxt.extract_sub<std::int8_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::uint8_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::int16_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::uint16_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::int32_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::uint32_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::int64_t>(val, "i"));
    ensure_eq(5, cxt.extract_sub<std::uint64_t>(val, "i"));
    ensure_eq(4.5f, cxt.extract_sub<float>(val, "d"));
    ensure_eq(4.5, cxt.extract_sub<double>(val, "d"));
    ensure_eq("10", cxt.extract_sub<std::string>(val, "s"));

    // some coercing...
    ensure_eq("5", cxt.extract_sub<std::string>(val, "i"));
    ensure_eq(10, cxt.extract_sub<int>(val, "s"));
}

// Tests that even if we throw a completely bogus exception type, the extraction_context wraps it in an extraction_error
TEST(extractor_throws_random_thing)
{
    static auto instance = make_function_extractor([] (const value& from) -> unassociated { throw from; });
    formats locals;
    locals.register_extractor(&instance);

    value val = object({ { "a", 1 } });

    extraction_context cxt(locals);
    ensure_throws(extraction_error, cxt.extract<unassociated>(val));
    ensure_throws(extraction_error, cxt.extract_sub<unassociated>(val, "a"));
}

}
<|endoftext|>


<commit_before>/*
    RawSpeed - RAW file decoder.

    Copyright (C) 2009-2014 Klaus Post
    Copyright (C) 2014-2015 Pedro Côrte-Real

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "decoders/RafDecoder.h"
#include "common/Common.h"                          // for uint32, ushort16
#include "common/Point.h"                           // for iPoint2D, iRecta...
#include "decoders/RawDecoderException.h"           // for RawDecoderExcept...
#include "decompressors/UncompressedDecompressor.h" // for UncompressedDeco...
#include "io/Buffer.h"                              // for Buffer
#include "io/ByteStream.h"                          // for ByteStream
#include "io/Endianness.h"                          // for getHostEndianness
#include "metadata/BlackArea.h"                     // for BlackArea
#include "metadata/Camera.h"                        // for Camera, Hints
#include "metadata/CameraMetaData.h"                // for CameraMetaData
#include "metadata/CameraSensorInfo.h"              // for CameraSensorInfo
#include "metadata/ColorFilterArray.h"              // for ColorFilterArray
#include "tiff/TiffEntry.h"                         // for TiffEntry
#include "tiff/TiffIFD.h"                           // for TiffRootIFD, Tif...
#include "tiff/TiffTag.h"                           // for TiffTag::FUJIOLDWB
#include <cassert>                                  // for assert
#include <cstdio>                                   // for size_t
#include <cstring>                                  // for memcmp
#include <memory>                                   // for unique_ptr, allo...
#include <string>                                   // for string
#include <vector>                                   // for vector

namespace rawspeed {

bool RafDecoder::isRAF(const Buffer* input) {
  static const char magic[] = "FUJIFILMCCD-RAW ";
  static const size_t magic_size = sizeof(magic) - 1; // excluding \0
  const unsigned char* data = input->getData(0, magic_size);
  return 0 == memcmp(&data[0], magic, magic_size);
}

bool RafDecoder::isAppropriateDecoder(const TiffRootIFD* rootIFD,
                                      const Buffer* file) {
  const auto id = rootIFD->getID();
  const std::string& make = id.make;

  // FIXME: magic

  return make == "FUJIFILM";
}

RawImage RafDecoder::decodeRawInternal() {
  auto raw = mRootIFD->getIFDWithTag(FUJI_STRIPOFFSETS);
  uint32 height = 0;
  uint32 width = 0;

  if (raw->hasEntry(FUJI_RAWIMAGEFULLHEIGHT)) {
    height = raw->getEntry(FUJI_RAWIMAGEFULLHEIGHT)->getU32();
    width = raw->getEntry(FUJI_RAWIMAGEFULLWIDTH)->getU32();
  } else if (raw->hasEntry(IMAGEWIDTH)) {
    TiffEntry *e = raw->getEntry(IMAGEWIDTH);
    height = e->getU16(0);
    width = e->getU16(1);
  } else
    ThrowRDE("Unable to locate image size");

  if (raw->hasEntry(FUJI_LAYOUT)) {
    TiffEntry *e = raw->getEntry(FUJI_LAYOUT);
    alt_layout = !(e->getByte(0) >> 7);
  }

  TiffEntry *offsets = raw->getEntry(FUJI_STRIPOFFSETS);
  TiffEntry *counts = raw->getEntry(FUJI_STRIPBYTECOUNTS);

  if (offsets->count != 1 || counts->count != 1)
    ThrowRDE("Multiple Strips found: %u %u", offsets->count, counts->count);

  if (counts->getU32() * 8 / (width * height) < 10)
    ThrowRDE("Don't know how to decode compressed images");

  int bps = 16;
  if (raw->hasEntry(FUJI_BITSPERSAMPLE))
    bps = raw->getEntry(FUJI_BITSPERSAMPLE)->getU32();

  ByteStream input(offsets->getRootIfdData());
  input = input.getSubStream(offsets->getU32(), counts->getU32());

  // x-trans sensors report 14bpp, but data isn't packed so read as 16bpp
  if (bps == 14)
    bps = 16;

  // Some fuji SuperCCD cameras include a second raw image next to the first one
  // that is identical but darker to the first. The two combined can produce
  // a higher dynamic range image. Right now we're ignoring it.
  bool double_width = hints.has("double_width_unpacked");

  mRaw->dim = iPoint2D(width*(double_width ? 2 : 1), height);
  mRaw->createData();

  UncompressedDecompressor u(input, mRaw);

  iPoint2D pos(0, 0);

  if (double_width) {
    u.decodeRawUnpacked<16, little>(width * 2, height);
  } else if (input.isInNativeByteOrder() == (getHostEndianness() == big)) {
    u.decodeRawUnpacked<16, big>(width, height);
  } else {
    if (hints.has("jpeg32_bitorder")) {
      u.readUncompressedRaw(mRaw->dim, pos, width * bps / 8, bps,
                            BitOrder_MSB32);
    } else {
      u.readUncompressedRaw(mRaw->dim, pos, width * bps / 8, bps, BitOrder_LSB);
    }
  }

  return mRaw;
}

void RafDecoder::checkSupportInternal(const CameraMetaData* meta) {
  if (!this->checkCameraSupported(meta, mRootIFD->getID(), ""))
    ThrowRDE("Unknown camera. Will not guess.");
}

void RafDecoder::decodeMetaDataInternal(const CameraMetaData* meta) {
  int iso = 0;
  if (mRootIFD->hasEntryRecursive(ISOSPEEDRATINGS))
    iso = mRootIFD->getEntryRecursive(ISOSPEEDRATINGS)->getU32();
  mRaw->metadata.isoSpeed = iso;

  // This is where we'd normally call setMetaData but since we may still need
  // to rotate the image for SuperCCD cameras we do everything ourselves
  auto id = mRootIFD->getID();
  const Camera* cam = meta->getCamera(id.make, id.model, "");
  if (!cam)
    ThrowRDE("Couldn't find camera");

  assert(cam != nullptr);

  iPoint2D new_size(mRaw->dim);
  iPoint2D crop_offset = iPoint2D(0,0);

  if (applyCrop) {
    new_size = cam->cropSize;
    crop_offset = cam->cropPos;
    bool double_width = hints.has("double_width_unpacked");
    // If crop size is negative, use relative cropping
    if (new_size.x <= 0)
      new_size.x = mRaw->dim.x / (double_width ? 2 : 1) - cam->cropPos.x + new_size.x;
    else
      new_size.x /= (double_width ? 2 : 1);
    if (new_size.y <= 0)
      new_size.y = mRaw->dim.y - cam->cropPos.y + new_size.y;
  }

  bool rotate = hints.has("fuji_rotate");
  rotate = rotate && fujiRotate;

  // Rotate 45 degrees - could be multithreaded.
  if (rotate && !this->uncorrectedRawValues) {
    // Calculate the 45 degree rotated size;
    uint32 rotatedsize;
    uint32 rotationPos;
    if (alt_layout) {
      rotatedsize = new_size.y+new_size.x/2;
      rotationPos = new_size.x/2 - 1;
    }
    else {
      rotatedsize = new_size.x+new_size.y/2;
      rotationPos = new_size.x - 1;
    }

    iPoint2D final_size(rotatedsize, rotatedsize-1);
    RawImage rotated = RawImage::create(final_size, TYPE_USHORT16, 1);
    rotated->clearArea(iRectangle2D(iPoint2D(0,0), rotated->dim));
    rotated->metadata = mRaw->metadata;
    rotated->metadata.fujiRotationPos = rotationPos;

    int dest_pitch = static_cast<int>(rotated->pitch) / 2;
    auto* dst = reinterpret_cast<ushort16*>(rotated->getData(0, 0));

    for (int y = 0; y < new_size.y; y++) {
      auto* src = reinterpret_cast<ushort16*>(
          mRaw->getData(crop_offset.x, crop_offset.y + y));
      for (int x = 0; x < new_size.x; x++) {
        int h;
        int w;
        if (alt_layout) { // Swapped x and y
          h = rotatedsize - (new_size.y + 1 - y + (x >> 1));
          w = ((x+1) >> 1) + y;
        } else {
          h = new_size.x - 1 - x + (y >> 1);
          w = ((y+1) >> 1) + x;
        }
        if (h < rotated->dim.y && w < rotated->dim.x)
          dst[w + h * dest_pitch] = src[x];
        else
          ThrowRDE("Trying to write out of bounds");
      }
    }
    mRaw = rotated;
  } else if (applyCrop) {
    mRaw->subFrame(iRectangle2D(crop_offset, new_size));
  }

  const CameraSensorInfo *sensor = cam->getSensorInfo(iso);
  mRaw->blackLevel = sensor->mBlackLevel;

  // at least the (bayer sensor) X100 comes with a tag like this:
  if (mRootIFD->hasEntryRecursive(FUJI_BLACKLEVEL)) {
    TiffEntry* sep_black = mRootIFD->getEntryRecursive(FUJI_BLACKLEVEL);
    if (sep_black->count == 4)
    {
      for(int k=0;k<4;k++)
        mRaw->blackLevelSeparate[k] = sep_black->getU32(k);
    } else if (sep_black->count == 36) {
      for (int& k : mRaw->blackLevelSeparate)
        k = 0;

      for (int y = 0; y < 6; y++) {
        for (int x = 0; x < 6; x++)
          mRaw->blackLevelSeparate[2 * (y % 2) + (x % 2)] +=
              sep_black->getU32(6 * y + x);
      }

      for (int& k : mRaw->blackLevelSeparate)
        k /= 9;
    }
  }

  mRaw->whitePoint = sensor->mWhiteLevel;
  mRaw->blackAreas = cam->blackAreas;
  mRaw->cfa = cam->cfa;
  mRaw->metadata.canonical_make = cam->canonical_make;
  mRaw->metadata.canonical_model = cam->canonical_model;
  mRaw->metadata.canonical_alias = cam->canonical_alias;
  mRaw->metadata.canonical_id = cam->canonical_id;
  mRaw->metadata.make = id.make;
  mRaw->metadata.model = id.model;

  if (mRootIFD->hasEntryRecursive(FUJI_WB_GRBLEVELS)) {
    TiffEntry *wb = mRootIFD->getEntryRecursive(FUJI_WB_GRBLEVELS);
    if (wb->count == 3) {
      mRaw->metadata.wbCoeffs[0] = wb->getFloat(1);
      mRaw->metadata.wbCoeffs[1] = wb->getFloat(0);
      mRaw->metadata.wbCoeffs[2] = wb->getFloat(2);
    }
  } else if (mRootIFD->hasEntryRecursive(FUJIOLDWB)) {
    TiffEntry *wb = mRootIFD->getEntryRecursive(FUJIOLDWB);
    if (wb->count == 8) {
      mRaw->metadata.wbCoeffs[0] = wb->getFloat(1);
      mRaw->metadata.wbCoeffs[1] = wb->getFloat(0);
      mRaw->metadata.wbCoeffs[2] = wb->getFloat(3);
    }
  }
}

} // namespace rawspeed
<commit_msg>RafDecoder: autodetect bps. Better than adjusting it sometimes.<commit_after>/*
    RawSpeed - RAW file decoder.

    Copyright (C) 2009-2014 Klaus Post
    Copyright (C) 2014-2015 Pedro Côrte-Real

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "decoders/RafDecoder.h"
#include "common/Common.h"                          // for uint32, ushort16
#include "common/Point.h"                           // for iPoint2D, iRecta...
#include "decoders/RawDecoderException.h"           // for RawDecoderExcept...
#include "decompressors/UncompressedDecompressor.h" // for UncompressedDeco...
#include "io/Buffer.h"                              // for Buffer
#include "io/ByteStream.h"                          // for ByteStream
#include "io/Endianness.h"                          // for getHostEndianness
#include "metadata/BlackArea.h"                     // for BlackArea
#include "metadata/Camera.h"                        // for Camera, Hints
#include "metadata/CameraMetaData.h"                // for CameraMetaData
#include "metadata/CameraSensorInfo.h"              // for CameraSensorInfo
#include "metadata/ColorFilterArray.h"              // for ColorFilterArray
#include "tiff/TiffEntry.h"                         // for TiffEntry
#include "tiff/TiffIFD.h"                           // for TiffRootIFD, Tif...
#include "tiff/TiffTag.h"                           // for TiffTag::FUJIOLDWB
#include <cassert>                                  // for assert
#include <cstdio>                                   // for size_t
#include <cstring>                                  // for memcmp
#include <memory>                                   // for unique_ptr, allo...
#include <string>                                   // for string
#include <vector>                                   // for vector

namespace rawspeed {

bool RafDecoder::isRAF(const Buffer* input) {
  static const char magic[] = "FUJIFILMCCD-RAW ";
  static const size_t magic_size = sizeof(magic) - 1; // excluding \0
  const unsigned char* data = input->getData(0, magic_size);
  return 0 == memcmp(&data[0], magic, magic_size);
}

bool RafDecoder::isAppropriateDecoder(const TiffRootIFD* rootIFD,
                                      const Buffer* file) {
  const auto id = rootIFD->getID();
  const std::string& make = id.make;

  // FIXME: magic

  return make == "FUJIFILM";
}

RawImage RafDecoder::decodeRawInternal() {
  auto raw = mRootIFD->getIFDWithTag(FUJI_STRIPOFFSETS);
  uint32 height = 0;
  uint32 width = 0;

  if (raw->hasEntry(FUJI_RAWIMAGEFULLHEIGHT)) {
    height = raw->getEntry(FUJI_RAWIMAGEFULLHEIGHT)->getU32();
    width = raw->getEntry(FUJI_RAWIMAGEFULLWIDTH)->getU32();
  } else if (raw->hasEntry(IMAGEWIDTH)) {
    TiffEntry *e = raw->getEntry(IMAGEWIDTH);
    height = e->getU16(0);
    width = e->getU16(1);
  } else
    ThrowRDE("Unable to locate image size");

  if (raw->hasEntry(FUJI_LAYOUT)) {
    TiffEntry *e = raw->getEntry(FUJI_LAYOUT);
    alt_layout = !(e->getByte(0) >> 7);
  }

  TiffEntry *offsets = raw->getEntry(FUJI_STRIPOFFSETS);
  TiffEntry *counts = raw->getEntry(FUJI_STRIPBYTECOUNTS);

  if (offsets->count != 1 || counts->count != 1)
    ThrowRDE("Multiple Strips found: %u %u", offsets->count, counts->count);

  if (counts->getU32() * 8 / (width * height) < 10)
    ThrowRDE("Don't know how to decode compressed images");

  ByteStream input(offsets->getRootIfdData());
  input = input.getSubStream(offsets->getU32(), counts->getU32());

  // x-trans sensors report 14bpp, but data isn't packed
  // thus, unless someone has any better ideas, let's autodetect it.
  int bps;

  // Some fuji SuperCCD cameras include a second raw image next to the first one
  // that is identical but darker to the first. The two combined can produce
  // a higher dynamic range image. Right now we're ignoring it.
  bool double_width;

  if (8UL * counts->getU32() >= 2UL * 16UL * width * height) {
    bps = 16;
    double_width = true;
  } else if (8UL * counts->getU32() >= 2UL * 14UL * width * height) {
    bps = 14;
    double_width = true;
  } else if (8UL * counts->getU32() >= 2UL * 12UL * width * height) {
    bps = 12;
    double_width = true;
  } else if (8UL * counts->getU32() >= 16UL * width * height) {
    bps = 16;
    double_width = false;
  } else if (8UL * counts->getU32() >= 14UL * width * height) {
    bps = 14;
    double_width = false;
  } else if (8UL * counts->getU32() >= 12UL * width * height) {
    bps = 12;
    double_width = false;
  } else
    ThrowRDE("Can not detect bitdepth. StripByteCounts = %u, width = %u, "
             "height = %u",
             counts->getU32(), width, height);

  double_width = hints.has("double_width_unpacked");
  const uint32 real_width = double_width ? 2U * width : width;

  mRaw->dim = iPoint2D(real_width, height);
  mRaw->createData();

  UncompressedDecompressor u(input, mRaw);

  iPoint2D pos(0, 0);

  if (double_width) {
    u.decodeRawUnpacked<16, little>(width * 2, height);
  } else if (input.isInNativeByteOrder() == (getHostEndianness() == big)) {
    u.decodeRawUnpacked<16, big>(width, height);
  } else {
    if (hints.has("jpeg32_bitorder")) {
      u.readUncompressedRaw(mRaw->dim, pos, width * bps / 8, bps,
                            BitOrder_MSB32);
    } else {
      u.readUncompressedRaw(mRaw->dim, pos, width * bps / 8, bps, BitOrder_LSB);
    }
  }

  return mRaw;
}

void RafDecoder::checkSupportInternal(const CameraMetaData* meta) {
  if (!this->checkCameraSupported(meta, mRootIFD->getID(), ""))
    ThrowRDE("Unknown camera. Will not guess.");
}

void RafDecoder::decodeMetaDataInternal(const CameraMetaData* meta) {
  int iso = 0;
  if (mRootIFD->hasEntryRecursive(ISOSPEEDRATINGS))
    iso = mRootIFD->getEntryRecursive(ISOSPEEDRATINGS)->getU32();
  mRaw->metadata.isoSpeed = iso;

  // This is where we'd normally call setMetaData but since we may still need
  // to rotate the image for SuperCCD cameras we do everything ourselves
  auto id = mRootIFD->getID();
  const Camera* cam = meta->getCamera(id.make, id.model, "");
  if (!cam)
    ThrowRDE("Couldn't find camera");

  assert(cam != nullptr);

  iPoint2D new_size(mRaw->dim);
  iPoint2D crop_offset = iPoint2D(0,0);

  if (applyCrop) {
    new_size = cam->cropSize;
    crop_offset = cam->cropPos;
    bool double_width = hints.has("double_width_unpacked");
    // If crop size is negative, use relative cropping
    if (new_size.x <= 0)
      new_size.x = mRaw->dim.x / (double_width ? 2 : 1) - cam->cropPos.x + new_size.x;
    else
      new_size.x /= (double_width ? 2 : 1);
    if (new_size.y <= 0)
      new_size.y = mRaw->dim.y - cam->cropPos.y + new_size.y;
  }

  bool rotate = hints.has("fuji_rotate");
  rotate = rotate && fujiRotate;

  // Rotate 45 degrees - could be multithreaded.
  if (rotate && !this->uncorrectedRawValues) {
    // Calculate the 45 degree rotated size;
    uint32 rotatedsize;
    uint32 rotationPos;
    if (alt_layout) {
      rotatedsize = new_size.y+new_size.x/2;
      rotationPos = new_size.x/2 - 1;
    }
    else {
      rotatedsize = new_size.x+new_size.y/2;
      rotationPos = new_size.x - 1;
    }

    iPoint2D final_size(rotatedsize, rotatedsize-1);
    RawImage rotated = RawImage::create(final_size, TYPE_USHORT16, 1);
    rotated->clearArea(iRectangle2D(iPoint2D(0,0), rotated->dim));
    rotated->metadata = mRaw->metadata;
    rotated->metadata.fujiRotationPos = rotationPos;

    int dest_pitch = static_cast<int>(rotated->pitch) / 2;
    auto* dst = reinterpret_cast<ushort16*>(rotated->getData(0, 0));

    for (int y = 0; y < new_size.y; y++) {
      auto* src = reinterpret_cast<ushort16*>(
          mRaw->getData(crop_offset.x, crop_offset.y + y));
      for (int x = 0; x < new_size.x; x++) {
        int h;
        int w;
        if (alt_layout) { // Swapped x and y
          h = rotatedsize - (new_size.y + 1 - y + (x >> 1));
          w = ((x+1) >> 1) + y;
        } else {
          h = new_size.x - 1 - x + (y >> 1);
          w = ((y+1) >> 1) + x;
        }
        if (h < rotated->dim.y && w < rotated->dim.x)
          dst[w + h * dest_pitch] = src[x];
        else
          ThrowRDE("Trying to write out of bounds");
      }
    }
    mRaw = rotated;
  } else if (applyCrop) {
    mRaw->subFrame(iRectangle2D(crop_offset, new_size));
  }

  const CameraSensorInfo *sensor = cam->getSensorInfo(iso);
  mRaw->blackLevel = sensor->mBlackLevel;

  // at least the (bayer sensor) X100 comes with a tag like this:
  if (mRootIFD->hasEntryRecursive(FUJI_BLACKLEVEL)) {
    TiffEntry* sep_black = mRootIFD->getEntryRecursive(FUJI_BLACKLEVEL);
    if (sep_black->count == 4)
    {
      for(int k=0;k<4;k++)
        mRaw->blackLevelSeparate[k] = sep_black->getU32(k);
    } else if (sep_black->count == 36) {
      for (int& k : mRaw->blackLevelSeparate)
        k = 0;

      for (int y = 0; y < 6; y++) {
        for (int x = 0; x < 6; x++)
          mRaw->blackLevelSeparate[2 * (y % 2) + (x % 2)] +=
              sep_black->getU32(6 * y + x);
      }

      for (int& k : mRaw->blackLevelSeparate)
        k /= 9;
    }
  }

  mRaw->whitePoint = sensor->mWhiteLevel;
  mRaw->blackAreas = cam->blackAreas;
  mRaw->cfa = cam->cfa;
  mRaw->metadata.canonical_make = cam->canonical_make;
  mRaw->metadata.canonical_model = cam->canonical_model;
  mRaw->metadata.canonical_alias = cam->canonical_alias;
  mRaw->metadata.canonical_id = cam->canonical_id;
  mRaw->metadata.make = id.make;
  mRaw->metadata.model = id.model;

  if (mRootIFD->hasEntryRecursive(FUJI_WB_GRBLEVELS)) {
    TiffEntry *wb = mRootIFD->getEntryRecursive(FUJI_WB_GRBLEVELS);
    if (wb->count == 3) {
      mRaw->metadata.wbCoeffs[0] = wb->getFloat(1);
      mRaw->metadata.wbCoeffs[1] = wb->getFloat(0);
      mRaw->metadata.wbCoeffs[2] = wb->getFloat(2);
    }
  } else if (mRootIFD->hasEntryRecursive(FUJIOLDWB)) {
    TiffEntry *wb = mRootIFD->getEntryRecursive(FUJIOLDWB);
    if (wb->count == 8) {
      mRaw->metadata.wbCoeffs[0] = wb->getFloat(1);
      mRaw->metadata.wbCoeffs[1] = wb->getFloat(0);
      mRaw->metadata.wbCoeffs[2] = wb->getFloat(3);
    }
  }
}

} // namespace rawspeed
<|endoftext|>