<commit_before>/***************************************************************************
 *   Copyright (C) 2006 by Tobias Koenig <tokoe@kde.org>                   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License as       *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This program 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 General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this program; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
 ***************************************************************************/

#include "dbinitializer.h"

#include <QtCore/QDebug>
#include <QtCore/QFile>
#include <QtCore/QList>
#include <QtCore/QPair>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
#include <QtSql/QSqlField>
#include <QtSql/QSqlRecord>
#include <QtSql/QSqlQuery>
#include <QtXml/QDomDocument>
#include <QtXml/QDomElement>

DbInitializer::DbInitializer( const QSqlDatabase &database, const QString &templateFile )
  : mDatabase( database ), mTemplateFile( templateFile )
{
}

DbInitializer::~DbInitializer()
{
}

bool DbInitializer::run()
{
  qDebug() << "DbInitializer::run()";

  QFile file( mTemplateFile );
  if ( !file.open( QIODevice::ReadOnly ) ) {
    mErrorMsg = QString::fromLatin1( "Unable to open template file '%1'." ).arg( mTemplateFile );
    return false;
  }

  QDomDocument document;

  QString errorMsg;
  int line, column;
  if ( !document.setContent( &file, &errorMsg, &line, &column ) ) {
    mErrorMsg = QString::fromLatin1( "Unable to parse template file '%1': %2 (%3:%4)." )
                       .arg( mTemplateFile ).arg( errorMsg ).arg( line ).arg( column );
    return false;
  }

  const QDomElement documentElement = document.documentElement();
  if ( documentElement.tagName() != QLatin1String( "database" ) ) {
    mErrorMsg = QString::fromLatin1( "Invalid format of template file '%1'." ).arg( mTemplateFile );
    return false;
  }

  QDomElement tableElement = documentElement.firstChildElement();
  while ( !tableElement.isNull() ) {
    if ( tableElement.tagName() == QLatin1String( "table" ) ) {
      if ( !checkTable( tableElement ) )
        return false;
    } else if ( tableElement.tagName() == QLatin1String( "relation" ) ) {
      if ( !checkRelation( tableElement ) )
        return false;
    }else {
      mErrorMsg = QString::fromLatin1( "Unknown tag, expected <table> and got <%1>." ).arg( tableElement.tagName() );
      return false;
    }

    tableElement = tableElement.nextSiblingElement();
  }

  qDebug() << "DbInitializer::run() done";
  return true;
}

bool DbInitializer::checkTable( const QDomElement &element )
{
  const QString tableName = element.attribute( QLatin1String("name") ) + QLatin1String("Table");

  qDebug() << "checking table " << tableName;

  typedef QPair<QString, QString> ColumnEntry;

  QList<ColumnEntry> columnsList;
  QStringList dataList;

  QDomElement columnElement = element.firstChildElement();
  while ( !columnElement.isNull() ) {
    if ( columnElement.tagName() == QLatin1String( "column" ) ) {
      ColumnEntry entry;
      entry.first = columnElement.attribute( QLatin1String("name") );
      if ( columnElement.attribute( QLatin1String("sqltype") ).isEmpty() )
        entry.second = sqlType( columnElement.attribute( QLatin1String("type") ) ) + QLatin1String( " " ) + columnElement.attribute( QLatin1String("properties") );
      else
        entry.second = columnElement.attribute( QLatin1String("sqltype") ) + QLatin1String( " " ) + columnElement.attribute( QLatin1String("properties") );
      if ( mDatabase.driverName() == QLatin1String( "QPSQL" ) ) {
        if ( entry.second.contains( QLatin1String("AUTOINCREMENT") ) )
          entry.second = QLatin1String("SERIAL PRIMARY KEY NOT NULL");
        if ( entry.second.contains( QLatin1String("BLOB") ) )
          entry.second = QLatin1String("BYTEA");
        if ( entry.second.startsWith( QLatin1String("CHAR") ) )
          entry.second.replace(QLatin1String("CHAR"), QLatin1String("VARCHAR"));
      } else if ( mDatabase.driverName().startsWith( QLatin1String("QMYSQL") ) ) {
        if ( entry.second.contains( QLatin1String("AUTOINCREMENT") ) )
          entry.second.replace(QLatin1String("AUTOINCREMENT"), QLatin1String("AUTO_INCREMENT"));
        if ( entry.second.startsWith( QLatin1String("CHAR") ) )
          entry.second.replace(QLatin1String("CHAR"), QLatin1String("VARCHAR"));
      }
      columnsList.append( entry );
    } else if ( columnElement.tagName() == QLatin1String( "data" ) ) {
      QString values = columnElement.attribute( QLatin1String("values") );
      if ( mDatabase.driverName().startsWith( QLatin1String("QMYSQL") ) )
        values.replace( QLatin1String("\\"), QLatin1String("\\\\") );
      QString statement = QString::fromLatin1( "INSERT INTO %1 (%2) VALUES (%3)" )
          .arg( tableName )
          .arg( columnElement.attribute( QLatin1String("columns") ) )
          .arg( values );
      dataList << statement;
    }

    columnElement = columnElement.nextSiblingElement();
  }

  QSqlQuery query( mDatabase );

  if ( !hasTable( tableName ) ) {
    /**
     * We have to create the entire table.
     */

    QString columns;
    for ( int i = 0; i < columnsList.count(); ++i ) {
      if ( i != 0 )
        columns.append( QLatin1String(", ") );

      columns.append( columnsList[ i ].first + QLatin1Char(' ') + columnsList[ i ].second );
    }

    /**
     * Add optional extra table properties (such as foreign keys and cascaded updates/deletes)
     */
    if( element.hasAttribute( QLatin1String("properties") ) )
      columns.append( QLatin1String(", ") + element.attribute( QLatin1String("properties") ) );

    const QString statement = QString::fromLatin1( "CREATE TABLE %1 (%2);" ).arg( tableName, columns );
    qDebug() << statement;

    if ( !query.exec( statement ) ) {
      mErrorMsg = QLatin1String( "Unable to create entire table." );
      return false;
    }
  } else {
    /**
     * Check for every column whether it exists.
     */

    const QSqlRecord table = mDatabase.record( tableName );

    for ( int i = 0; i < columnsList.count(); ++i ) {
      const ColumnEntry entry = columnsList[ i ];

      bool found = false;
      for ( int j = 0; j < table.count(); ++j ) {
        const QSqlField column = table.field( j );

        if ( columnsList[ i ].first.toLower() == column.name().toLower() ) {
          found = true;
        }
      }

      if ( !found ) {
        /**
         * Add missing column to table.
         */
        const QString statement = QString::fromLatin1( "ALTER TABLE %1 ADD COLUMN %2 %3;" )
                                         .arg( tableName, entry.first, entry.second );

        if ( !query.exec( statement ) ) {
          mErrorMsg = QString::fromLatin1( "Unable to add column '%1' to table '%2'." ).arg( entry.first, tableName );
          return false;
        }
      }
    }

    // TODO: remove obsolete columns (when sqlite will support it) and adapt column type modifications
  }

  // add indices
  columnElement = element.firstChildElement();
  while ( !columnElement.isNull() ) {
    if ( columnElement.tagName() == QLatin1String( "index" ) ) {
      if ( !hasIndex( tableName, columnElement.attribute( QLatin1String("name") ) ) ) {
        QString statement = QLatin1String( "ALTER TABLE " );
        statement += tableName;
        statement += QLatin1String( " ADD " );
        if ( columnElement.attribute( QLatin1String("unique") ) == QLatin1String( "true" ) )
          statement += QLatin1String( "UNIQUE " );
        else
          statement += QLatin1String( "INDEX  " );
        statement += columnElement.attribute( QLatin1String("name") );
        statement += QLatin1String( " (" );
        statement += columnElement.attribute( QLatin1String("columns") );
        statement += QLatin1Char(')');
        QSqlQuery query( mDatabase );
        qDebug() << "adding index" << statement;
        if ( !query.exec( statement ) ) {
          mErrorMsg = QLatin1String( "Unable to create index." );
          return false;
        }
      }
    }
    columnElement = columnElement.nextSiblingElement();
  }

  // add initial data if table is empty
  const QString statement = QString::fromLatin1( "SELECT * FROM %1 LIMIT 1" ).arg( tableName );
  if ( !query.exec( statement ) ) {
    mErrorMsg = QString::fromLatin1( "Unable to retrieve data from table '%1'." ).arg( tableName );
    return false;
  }
  if ( query.size() == 0  || !query.first() ) {
    foreach ( QString stmt, dataList ) {
      if ( !query.exec( stmt ) ) {
        mErrorMsg = QString::fromLatin1( "Unable to add initial data to table '%1'." ).arg( tableName );
        mErrorMsg += QString::fromLatin1( "Query was: %1" ).arg( stmt );
        return false;
      }
    }
  }

  return true;
}

bool DbInitializer::checkRelation(const QDomElement & element)
{
  QString table1 = element.attribute(QLatin1String("table1"));
  QString table2 = element.attribute(QLatin1String("table2"));
  QString col1 = element.attribute(QLatin1String("column1"));
  QString col2 = element.attribute(QLatin1String("column2"));

  QString tableName = table1 + table2 + QLatin1String("Relation");
  qDebug() << "checking relation " << tableName;

  if ( !hasTable( tableName ) ) {
    QString statement = QString::fromLatin1( "CREATE TABLE %1 (" ).arg( tableName );
    statement += QString::fromLatin1("%1_%2 INTEGER REFERENCES %3(%4), " )
        .arg( table1 )
        .arg( col1 )
        .arg( table1 )
        .arg( col1 );
    statement += QString::fromLatin1("%1_%2 INTEGER REFERENCES %3(%4))" )
        .arg( table2 )
        .arg( col2 )
        .arg( table2 )
        .arg( col2 );
    qDebug() << statement;

    QSqlQuery query( mDatabase );
    if ( !query.exec( statement ) ) {
      mErrorMsg = QLatin1String( "Unable to create entire table." );
      return false;
    }
  }

  if ( !hasIndex( tableName, QLatin1String("PRIMARY") ) ) {
    QString statement = QLatin1String( "ALTER TABLE " );
    statement += tableName;
    statement += QLatin1String( " ADD PRIMARY KEY (" );
    statement += QString::fromLatin1( "%1_%2" ).arg( table1 ).arg( col1 );
    statement += QLatin1String( ", " );
    statement += QString::fromLatin1( "%1_%2" ).arg( table2 ).arg( col2 );
    statement += QLatin1Char(')');
    QSqlQuery query( mDatabase );
    qDebug() << "adding index" << statement;
    if ( !query.exec( statement ) ) {
      mErrorMsg = QLatin1String( "Unable to create index." );
      return false;
    }
  }

  return true;
}

QString DbInitializer::errorMsg() const
{
  return mErrorMsg;
}

QString DbInitializer::sqlType(const QString & type)
{
  if ( type == QLatin1String("int") )
    return QLatin1String("INTEGER");
  if ( type == QLatin1String("qint64") )
    return QLatin1String("BIGINT");
  if ( type == QLatin1String("QString") )
    return QLatin1String("TEXT");
  if (type == QLatin1String("QByteArray") )
    return QLatin1String("LONGBLOB");
  if ( type == QLatin1String("QDateTime") )
    return QLatin1String("TIMESTAMP");
  if ( type == QLatin1String( "bool" ) )
    return QLatin1String("BOOL");
  Q_ASSERT( false );
  return QString();
}

bool DbInitializer::hasTable(const QString & tableName)
{
  QString statement;
  if ( mDatabase.driverName() == QLatin1String( "QPSQL" ) )
    statement = QLatin1String("SELECT tablename FROM pg_tables ORDER BY tablename;" );
  else if ( mDatabase.driverName() == QLatin1String( "QSQLITE" ) )
    statement = QLatin1String("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;");
  else if ( mDatabase.driverName().startsWith( QLatin1String("QMYSQL") ) )
    statement = QLatin1String("SHOW TABLES");
  else
    Q_ASSERT( false ); // unknown driver

  QSqlQuery query( mDatabase );
  if ( !query.exec( statement ) ) {
    mErrorMsg = QLatin1String( "Unable to retrieve table information from database." );
    return false;
  }

  while ( query.next() ) {
    if ( query.value( 0 ).toString().toLower() == tableName.toLower() )
      return true;
  }
  return false;
}

bool DbInitializer::hasIndex(const QString & tableName, const QString & indexName)
{
  if ( mDatabase.driverName().startsWith( QLatin1String("QMYSQL") ) ) {
    QString statement = QString::fromLatin1("SHOW INDEXES FROM %1").arg( tableName );
    QSqlQuery query( mDatabase );
    if ( !query.exec( statement ) ) {
      mErrorMsg = QLatin1String( "Unable to list index information." );
      return false;
    }
    while ( query.next() ) {
      // FIXME: use column name (key_name) instead of column index!!
      if ( query.value(2) == indexName )
        return true;
    }
    return false;
  } else {
    qFatal("Implement index support for your database!");
  }
  return false;
}
<commit_msg>Patch by Tony Wolf <wolf@os-forge.net> to use standard SQL syntax instead of the MySQL specific one for creating indices.<commit_after>/***************************************************************************
 *   Copyright (C) 2006 by Tobias Koenig <tokoe@kde.org>                   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License as       *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This program 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 General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this program; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
 ***************************************************************************/

#include "dbinitializer.h"

#include <QtCore/QDebug>
#include <QtCore/QFile>
#include <QtCore/QList>
#include <QtCore/QPair>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
#include <QtSql/QSqlField>
#include <QtSql/QSqlRecord>
#include <QtSql/QSqlQuery>
#include <QtXml/QDomDocument>
#include <QtXml/QDomElement>

DbInitializer::DbInitializer( const QSqlDatabase &database, const QString &templateFile )
  : mDatabase( database ), mTemplateFile( templateFile )
{
}

DbInitializer::~DbInitializer()
{
}

bool DbInitializer::run()
{
  qDebug() << "DbInitializer::run()";

  QFile file( mTemplateFile );
  if ( !file.open( QIODevice::ReadOnly ) ) {
    mErrorMsg = QString::fromLatin1( "Unable to open template file '%1'." ).arg( mTemplateFile );
    return false;
  }

  QDomDocument document;

  QString errorMsg;
  int line, column;
  if ( !document.setContent( &file, &errorMsg, &line, &column ) ) {
    mErrorMsg = QString::fromLatin1( "Unable to parse template file '%1': %2 (%3:%4)." )
                       .arg( mTemplateFile ).arg( errorMsg ).arg( line ).arg( column );
    return false;
  }

  const QDomElement documentElement = document.documentElement();
  if ( documentElement.tagName() != QLatin1String( "database" ) ) {
    mErrorMsg = QString::fromLatin1( "Invalid format of template file '%1'." ).arg( mTemplateFile );
    return false;
  }

  QDomElement tableElement = documentElement.firstChildElement();
  while ( !tableElement.isNull() ) {
    if ( tableElement.tagName() == QLatin1String( "table" ) ) {
      if ( !checkTable( tableElement ) )
        return false;
    } else if ( tableElement.tagName() == QLatin1String( "relation" ) ) {
      if ( !checkRelation( tableElement ) )
        return false;
    }else {
      mErrorMsg = QString::fromLatin1( "Unknown tag, expected <table> and got <%1>." ).arg( tableElement.tagName() );
      return false;
    }

    tableElement = tableElement.nextSiblingElement();
  }

  qDebug() << "DbInitializer::run() done";
  return true;
}

bool DbInitializer::checkTable( const QDomElement &element )
{
  const QString tableName = element.attribute( QLatin1String("name") ) + QLatin1String("Table");

  qDebug() << "checking table " << tableName;

  typedef QPair<QString, QString> ColumnEntry;

  QList<ColumnEntry> columnsList;
  QStringList dataList;

  QDomElement columnElement = element.firstChildElement();
  while ( !columnElement.isNull() ) {
    if ( columnElement.tagName() == QLatin1String( "column" ) ) {
      ColumnEntry entry;
      entry.first = columnElement.attribute( QLatin1String("name") );
      if ( columnElement.attribute( QLatin1String("sqltype") ).isEmpty() )
        entry.second = sqlType( columnElement.attribute( QLatin1String("type") ) ) + QLatin1String( " " ) + columnElement.attribute( QLatin1String("properties") );
      else
        entry.second = columnElement.attribute( QLatin1String("sqltype") ) + QLatin1String( " " ) + columnElement.attribute( QLatin1String("properties") );
      if ( mDatabase.driverName() == QLatin1String( "QPSQL" ) ) {
        if ( entry.second.contains( QLatin1String("AUTOINCREMENT") ) )
          entry.second = QLatin1String("SERIAL PRIMARY KEY NOT NULL");
        if ( entry.second.contains( QLatin1String("BLOB") ) )
          entry.second = QLatin1String("BYTEA");
        if ( entry.second.startsWith( QLatin1String("CHAR") ) )
          entry.second.replace(QLatin1String("CHAR"), QLatin1String("VARCHAR"));
      } else if ( mDatabase.driverName().startsWith( QLatin1String("QMYSQL") ) ) {
        if ( entry.second.contains( QLatin1String("AUTOINCREMENT") ) )
          entry.second.replace(QLatin1String("AUTOINCREMENT"), QLatin1String("AUTO_INCREMENT"));
        if ( entry.second.startsWith( QLatin1String("CHAR") ) )
          entry.second.replace(QLatin1String("CHAR"), QLatin1String("VARCHAR"));
      }
      columnsList.append( entry );
    } else if ( columnElement.tagName() == QLatin1String( "data" ) ) {
      QString values = columnElement.attribute( QLatin1String("values") );
      if ( mDatabase.driverName().startsWith( QLatin1String("QMYSQL") ) )
        values.replace( QLatin1String("\\"), QLatin1String("\\\\") );
      QString statement = QString::fromLatin1( "INSERT INTO %1 (%2) VALUES (%3)" )
          .arg( tableName )
          .arg( columnElement.attribute( QLatin1String("columns") ) )
          .arg( values );
      dataList << statement;
    }

    columnElement = columnElement.nextSiblingElement();
  }

  QSqlQuery query( mDatabase );

  if ( !hasTable( tableName ) ) {
    /**
     * We have to create the entire table.
     */

    QString columns;
    for ( int i = 0; i < columnsList.count(); ++i ) {
      if ( i != 0 )
        columns.append( QLatin1String(", ") );

      columns.append( columnsList[ i ].first + QLatin1Char(' ') + columnsList[ i ].second );
    }

    /**
     * Add optional extra table properties (such as foreign keys and cascaded updates/deletes)
     */
    if( element.hasAttribute( QLatin1String("properties") ) )
      columns.append( QLatin1String(", ") + element.attribute( QLatin1String("properties") ) );

    const QString statement = QString::fromLatin1( "CREATE TABLE %1 (%2);" ).arg( tableName, columns );
    qDebug() << statement;

    if ( !query.exec( statement ) ) {
      mErrorMsg = QLatin1String( "Unable to create entire table." );
      return false;
    }
  } else {
    /**
     * Check for every column whether it exists.
     */

    const QSqlRecord table = mDatabase.record( tableName );

    for ( int i = 0; i < columnsList.count(); ++i ) {
      const ColumnEntry entry = columnsList[ i ];

      bool found = false;
      for ( int j = 0; j < table.count(); ++j ) {
        const QSqlField column = table.field( j );

        if ( columnsList[ i ].first.toLower() == column.name().toLower() ) {
          found = true;
        }
      }

      if ( !found ) {
        /**
         * Add missing column to table.
         */
        const QString statement = QString::fromLatin1( "ALTER TABLE %1 ADD COLUMN %2 %3;" )
                                         .arg( tableName, entry.first, entry.second );

        if ( !query.exec( statement ) ) {
          mErrorMsg = QString::fromLatin1( "Unable to add column '%1' to table '%2'." ).arg( entry.first, tableName );
          return false;
        }
      }
    }

    // TODO: remove obsolete columns (when sqlite will support it) and adapt column type modifications
  }

  // add indices
  columnElement = element.firstChildElement();
  while ( !columnElement.isNull() ) {
    if ( columnElement.tagName() == QLatin1String( "index" ) ) {
      if ( !hasIndex( tableName, columnElement.attribute( QLatin1String("name") ) ) ) {
        QString statement = QLatin1String( "CREATE " );
        if ( columnElement.attribute( QLatin1String("unique") ) == QLatin1String( "true" ) )
          statement += QLatin1String( "UNIQUE " );
        statement += QLatin1String( "INDEX " );
        statement += columnElement.attribute( QLatin1String("name") );
        statement += QLatin1String( " ON " );
        statement += tableName;
        statement += QLatin1String( " (" );
        statement += columnElement.attribute( QLatin1String("columns") );
        statement += QLatin1Char(')');
        QSqlQuery query( mDatabase );
        qDebug() << "adding index" << statement;
        if ( !query.exec( statement ) ) {
          mErrorMsg = QLatin1String( "Unable to create index." );
          return false;
        }
      }
    }
    columnElement = columnElement.nextSiblingElement();
  }

  // add initial data if table is empty
  const QString statement = QString::fromLatin1( "SELECT * FROM %1 LIMIT 1" ).arg( tableName );
  if ( !query.exec( statement ) ) {
    mErrorMsg = QString::fromLatin1( "Unable to retrieve data from table '%1'." ).arg( tableName );
    return false;
  }
  if ( query.size() == 0  || !query.first() ) {
    foreach ( QString stmt, dataList ) {
      if ( !query.exec( stmt ) ) {
        mErrorMsg = QString::fromLatin1( "Unable to add initial data to table '%1'." ).arg( tableName );
        mErrorMsg += QString::fromLatin1( "Query was: %1" ).arg( stmt );
        return false;
      }
    }
  }

  return true;
}

bool DbInitializer::checkRelation(const QDomElement & element)
{
  QString table1 = element.attribute(QLatin1String("table1"));
  QString table2 = element.attribute(QLatin1String("table2"));
  QString col1 = element.attribute(QLatin1String("column1"));
  QString col2 = element.attribute(QLatin1String("column2"));

  QString tableName = table1 + table2 + QLatin1String("Relation");
  qDebug() << "checking relation " << tableName;

  if ( !hasTable( tableName ) ) {
    QString statement = QString::fromLatin1( "CREATE TABLE %1 (" ).arg( tableName );
    statement += QString::fromLatin1("%1_%2 INTEGER REFERENCES %3(%4), " )
        .arg( table1 )
        .arg( col1 )
        .arg( table1 )
        .arg( col1 );
    statement += QString::fromLatin1("%1_%2 INTEGER REFERENCES %3(%4))" )
        .arg( table2 )
        .arg( col2 )
        .arg( table2 )
        .arg( col2 );
    qDebug() << statement;

    QSqlQuery query( mDatabase );
    if ( !query.exec( statement ) ) {
      mErrorMsg = QLatin1String( "Unable to create entire table." );
      return false;
    }
  }

  if ( !hasIndex( tableName, QLatin1String("PRIMARY") ) ) {
    QString statement = QLatin1String( "ALTER TABLE " );
    statement += tableName;
    statement += QLatin1String( " ADD PRIMARY KEY (" );
    statement += QString::fromLatin1( "%1_%2" ).arg( table1 ).arg( col1 );
    statement += QLatin1String( ", " );
    statement += QString::fromLatin1( "%1_%2" ).arg( table2 ).arg( col2 );
    statement += QLatin1Char(')');
    QSqlQuery query( mDatabase );
    qDebug() << "adding index" << statement;
    if ( !query.exec( statement ) ) {
      mErrorMsg = QLatin1String( "Unable to create index." );
      return false;
    }
  }

  return true;
}

QString DbInitializer::errorMsg() const
{
  return mErrorMsg;
}

QString DbInitializer::sqlType(const QString & type)
{
  if ( type == QLatin1String("int") )
    return QLatin1String("INTEGER");
  if ( type == QLatin1String("qint64") )
    return QLatin1String("BIGINT");
  if ( type == QLatin1String("QString") )
    return QLatin1String("TEXT");
  if (type == QLatin1String("QByteArray") )
    return QLatin1String("LONGBLOB");
  if ( type == QLatin1String("QDateTime") )
    return QLatin1String("TIMESTAMP");
  if ( type == QLatin1String( "bool" ) )
    return QLatin1String("BOOL");
  Q_ASSERT( false );
  return QString();
}

bool DbInitializer::hasTable(const QString & tableName)
{
  QString statement;
  if ( mDatabase.driverName() == QLatin1String( "QPSQL" ) )
    statement = QLatin1String("SELECT tablename FROM pg_tables ORDER BY tablename;" );
  else if ( mDatabase.driverName() == QLatin1String( "QSQLITE" ) )
    statement = QLatin1String("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;");
  else if ( mDatabase.driverName().startsWith( QLatin1String("QMYSQL") ) )
    statement = QLatin1String("SHOW TABLES");
  else
    Q_ASSERT( false ); // unknown driver

  QSqlQuery query( mDatabase );
  if ( !query.exec( statement ) ) {
    mErrorMsg = QLatin1String( "Unable to retrieve table information from database." );
    return false;
  }

  while ( query.next() ) {
    if ( query.value( 0 ).toString().toLower() == tableName.toLower() )
      return true;
  }
  return false;
}

bool DbInitializer::hasIndex(const QString & tableName, const QString & indexName)
{
  if ( mDatabase.driverName().startsWith( QLatin1String("QMYSQL") ) ) {
    QString statement = QString::fromLatin1("SHOW INDEXES FROM %1").arg( tableName );
    QSqlQuery query( mDatabase );
    if ( !query.exec( statement ) ) {
      mErrorMsg = QLatin1String( "Unable to list index information." );
      return false;
    }
    while ( query.next() ) {
      // FIXME: use column name (key_name) instead of column index!!
      if ( query.value(2) == indexName )
        return true;
    }
    return false;
  } else {
    qFatal("Implement index support for your database!");
  }
  return false;
}
<|endoftext|>


<commit_before>/*
 * RenderVolumeSlice.cpp
 *
 * Copyright (C) 2012 by Universitaet Stuttgart (VISUS).
 * Alle Rechte vorbehalten.
 */

#include "stdafx.h"
#include "RenderVolumeSlice.h"
#include "CallVolumeData.h"
#include "param/StringParam.h"
#include "param/FloatParam.h"
#include <climits>
#include <cfloat>
#include <cmath>
#include "CoreInstance.h"
#include "view/CallClipPlane.h"
#include "view/CallGetTransferFunction.h"
#include "view/CallRender3D.h"
#include <GL/gl.h>
#include "vislib/assert.h"

using namespace megamol::core;

/*
 * RenderVolumeSlice::RenderVolumeSlice
 */
RenderVolumeSlice::RenderVolumeSlice(void) : Renderer3DModule(),
        getVolSlot("getVol", "The call for data"),
        getTFSlot("gettransferfunction", "The call for Transfer function"),
        getClipPlaneSlot("getclipplane", "The call for clipping plane"),
        attributeSlot("attr", "The attribute to show"),
        lowValSlot("low", "The low value"),
        highValSlot("high", "The high value") {

    this->getVolSlot.SetCompatibleCall<CallVolumeDataDescription>();
    this->MakeSlotAvailable(&this->getVolSlot);

    this->getTFSlot.SetCompatibleCall<view::CallGetTransferFunctionDescription>();
    this->MakeSlotAvailable(&this->getTFSlot);

    this->getClipPlaneSlot.SetCompatibleCall<view::CallClipPlaneDescription>();
    this->MakeSlotAvailable(&this->getClipPlaneSlot);

    this->attributeSlot << new param::StringParam("0");
    this->MakeSlotAvailable(&this->attributeSlot);

    this->lowValSlot << new param::FloatParam(0.0f);
    this->MakeSlotAvailable(&this->lowValSlot);

    this->highValSlot << new param::FloatParam(1.0f);
    this->MakeSlotAvailable(&this->highValSlot);

}

/*
 * RenderVolumeSlice::RenderVolumeSlice
 */
RenderVolumeSlice::~RenderVolumeSlice(void) {
    this->Release();
}

/*
 * RenderVolumeSlice::RenderVolumeSlice
 */
bool RenderVolumeSlice::create(void) {
    // intentionally empty
    return true;
}

/*
 * RenderVolumeSlice::RenderVolumeSlice
 */
bool RenderVolumeSlice::GetCapabilities(Call& call) {
    view::CallRender3D *cr = dynamic_cast<view::CallRender3D*>(&call);
    if (cr == NULL) return false;

    cr->SetCapabilities(
        view::CallRender3D::CAP_RENDER
        //| view::CallRender3D::CAP_LIGHTING
        | view::CallRender3D::CAP_ANIMATION
        );

    return true;
}

/*
 * RenderVolumeSlice::RenderVolumeSlice
 */
bool RenderVolumeSlice::GetExtents(Call& call) {
    view::CallRender3D *cr = dynamic_cast<view::CallRender3D*>(&call);
    if (cr == NULL) return false;

    CallVolumeData *c2 = this->getVolSlot.CallAs<CallVolumeData>();
    if ((c2 != NULL) && ((*c2)(1))) {
        cr->SetTimeFramesCount(c2->FrameCount());
        cr->AccessBoundingBoxes() = c2->AccessBoundingBoxes();
        cr->AccessBoundingBoxes().MakeScaledWorld(1.0f);

    } else {
        cr->SetTimeFramesCount(1);
        cr->AccessBoundingBoxes().Clear();
    }

    return true;
}

/*
 * RenderVolumeSlice::RenderVolumeSlice
 */
void RenderVolumeSlice::release(void) {
    // intentionally empty
}

/*
 * RenderVolumeSlice::RenderVolumeSlice
 */
bool RenderVolumeSlice::Render(Call& call) {
    view::CallRender3D *cr = dynamic_cast<view::CallRender3D*>(&call);
    if (cr == NULL) return false;

    // get volume data
    CallVolumeData *cvd = this->getVolSlot.CallAs<CallVolumeData>();
    if (cvd == NULL) return false;
    cvd->SetFrameID(static_cast<unsigned int>(cr->Time()));
    if (!(*cvd)(1)) return false;
    vislib::math::Cuboid<float> bbox = cvd->GetBoundingBoxes().ObjectSpaceBBox();
    cvd->SetFrameID(static_cast<unsigned int>(cr->Time()));
    if (!(*cvd)(0)) return false;

    float stepSize, ssy, ssz;
    stepSize = bbox.Width() / static_cast<float>(cvd->XSize());
    ssy = bbox.Height() / static_cast<float>(cvd->YSize());
    ssz = bbox.Depth() / static_cast<float>(cvd->ZSize());
    stepSize = fabs(stepSize + ssy + ssz) / 3.0f;

    // find the volumed attribute
    vislib::StringA attrName(this->attributeSlot.Param<param::StringParam>()->Value());
    unsigned int attrIdx = cvd->FindAttribute(attrName);
    if (attrIdx == UINT_MAX) {
        try {
            attrIdx = static_cast<unsigned int>(vislib::CharTraitsA::ParseInt(attrName));
        } catch(...) {
            return false;
        }
    }
    ASSERT(attrIdx != UINT_MAX);

    // get clip plane
    view::CallClipPlane *ccp = this->getClipPlaneSlot.CallAs<view::CallClipPlane>();
    if ((ccp == NULL) || (!(*ccp)())) return false;

    // get transfer function
    view::CallGetTransferFunction *cgtf = this->getTFSlot.CallAs<view::CallGetTransferFunction>();
    if ((cgtf == NULL) || (!(*cgtf)())) cgtf = NULL;

    unsigned int colTabSize;
    if (cgtf != NULL) {
        ::glDisable(GL_TEXTURE_2D);
        ::glEnable(GL_TEXTURE_1D);
        ::glEnable(GL_COLOR_MATERIAL);
        ::glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
        ::glBindTexture(GL_TEXTURE_1D, cgtf->OpenGLTexture());
        colTabSize = cgtf->TextureSize();
    } else {
        ::glDisable(GL_TEXTURE_1D);
        ::glBindTexture(GL_TEXTURE_1D, 0);
        colTabSize = 2;
    }

    float minVal = this->lowValSlot.Param<param::FloatParam>()->Value();
    float maxVal = this->highValSlot.Param<param::FloatParam>()->Value();

    vislib::math::Point<float, 3> ps[12];
    bool psv[12];

    {
        vislib::math::Plane<float> minX(bbox.GetLeftBottomBack(), vislib::math::Vector<float, 3>(-1.0f, 0.0f, 0.0f));
        vislib::math::Plane<float> minY(bbox.GetLeftBottomBack(), vislib::math::Vector<float, 3>(0.0f, -1.0f, 0.0f));
        vislib::math::Plane<float> minZ(bbox.GetLeftBottomBack(), vislib::math::Vector<float, 3>(0.0f, 0.0f, -1.0f));
        vislib::math::Plane<float> maxX(bbox.GetRightTopFront(), vislib::math::Vector<float, 3>(1.0f, 0.0f, 0.0f));
        vislib::math::Plane<float> maxY(bbox.GetRightTopFront(), vislib::math::Vector<float, 3>(0.0f, 1.0f, 0.0f));
        vislib::math::Plane<float> maxZ(bbox.GetRightTopFront(), vislib::math::Vector<float, 3>(0.0f, 0.0f, 1.0f));

        psv[0] = ccp->GetPlane().CalcIntersectionPoint(minX, minY, ps[0]);
        psv[1] = ccp->GetPlane().CalcIntersectionPoint(minX, minZ, ps[1]);
        psv[2] = ccp->GetPlane().CalcIntersectionPoint(minX, maxY, ps[2]);
        psv[3] = ccp->GetPlane().CalcIntersectionPoint(minX, maxZ, ps[3]);
        psv[4] = ccp->GetPlane().CalcIntersectionPoint(maxX, minY, ps[4]);
        psv[5] = ccp->GetPlane().CalcIntersectionPoint(maxX, minZ, ps[5]);
        psv[6] = ccp->GetPlane().CalcIntersectionPoint(maxX, maxY, ps[6]);
        psv[7] = ccp->GetPlane().CalcIntersectionPoint(maxX, maxZ, ps[7]);
        psv[8] = ccp->GetPlane().CalcIntersectionPoint(minY, minZ, ps[8]);
        psv[9] = ccp->GetPlane().CalcIntersectionPoint(minY, maxZ, ps[9]);
        psv[10] = ccp->GetPlane().CalcIntersectionPoint(maxY, minZ, ps[10]);
        psv[11] = ccp->GetPlane().CalcIntersectionPoint(maxY, maxZ, ps[11]);
    }

    unsigned int cnt = 0;
    vislib::math::Vector<float, 3> v;
    for (int i = 0; i < 12; i++) {
        if (!psv[i]) continue;
        v += vislib::math::Vector<float, 3>(ps[i]);
        cnt++;
    }
    if (cnt < 3) return false;
    v /= static_cast<float>(cnt);

    vislib::math::Point<float, 3> orig(v.PeekComponents());

    vislib::math::Vector<float, 3> x, y, z;
    ccp->CalcPlaneSystem(x, y, z);

    float minX, maxX, minY, maxY;
    minX = minY = 0.0f;
    maxX = maxY = 0.0f;
    for (int i = 0; i < 12; i++) {
        if (!psv[i]) continue;
        v = ps[i] - orig;
        float xc = v.Dot(x);
        float yc = v.Dot(y);
        if (minX > xc) minX = xc;
        if (maxX < xc) maxX = xc;
        if (minY > yc) minY = yc;
        if (maxY < yc) maxY = yc;
    }
    // loop ranges
    int minXi = static_cast<int>(minX / stepSize);
    int minYi = static_cast<int>(minY / stepSize);
    int maxXi = static_cast<int>(maxX / stepSize);
    int maxYi = static_cast<int>(maxY / stepSize);
    int w = (maxXi - minXi) + 1;
    int h = (maxYi - minYi) + 1;

    vislib::math::Vector<float, 3> *pos = new vislib::math::Vector<float, 3>[w * h];
    float *val = new float[w * h];

    for (int ix = minXi; ix <= maxXi; ix++) {
        int io = ix - minXi;
        for (int iy = minYi; iy <= maxYi; iy++) {
            int idx = (iy - minYi) * w + io;
            pos[idx] = orig;
            v = x;
            v *= static_cast<float>(ix) * stepSize;
            pos[idx] += v;
            v = y;
            v *= static_cast<float>(iy) * stepSize;
            pos[idx] += v;

            int vix = static_cast<int>(0.5f + (pos[idx].X() - bbox.Left()) * static_cast<float>(cvd->XSize()) / bbox.Width());
            int viy = static_cast<int>(0.5f + (pos[idx].Y() - bbox.Bottom()) * static_cast<float>(cvd->YSize()) / bbox.Height());
            int viz = static_cast<int>(0.5f + (pos[idx].Z() - bbox.Back()) * static_cast<float>(cvd->ZSize()) / bbox.Depth());
            if (vix < 0) vix = 0;
            if (vix >= static_cast<int>(cvd->XSize())) vix = cvd->XSize() - 1;
            if (viy < 0) viy = 0;
            if (viy >= static_cast<int>(cvd->YSize())) viy = cvd->YSize() - 1;
            if (viz < 0) viz = 0;
            if (viz >= static_cast<int>(cvd->ZSize())) viz = cvd->ZSize() - 1;

            val[idx] = cvd->Attribute(attrIdx).Floats()[vix + cvd->XSize() * (viy + cvd->YSize() * viz)];
            val[idx] = (val[idx] - minVal) / (maxVal - minVal);
            if (val[idx] < 0.0f) val[idx] = 0.0f;
            if (val[idx] > 1.0f) val[idx] = 1.0f;
        }
    }

    ::glNormal3fv(z.PeekComponents());

    ::glDisable(GL_CULL_FACE);
    GLint lmts = 1;
    ::glLightModeliv(GL_LIGHT_MODEL_TWO_SIDE, &lmts);
    ::glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    ::glColor3ub(255, 255, 255);
    ::glTexCoord1f(0.0f);

    for (int ix = minXi; ix < maxXi; ix++) {
        ::glBegin(GL_TRIANGLE_STRIP);
        for (int iy = minYi; iy < maxYi; iy++) {
            int idx = (ix - minXi) + w * (iy - minYi);
            for (int ii = 0; ii < 4; ii++) {
                if (cgtf == NULL) {
                    ::glColor3f(val[idx], val[idx], val[idx]);
                } else {
                    ::glTexCoord1f(val[idx]);
                }
                ::glVertex3fv(pos[idx].PeekComponents());
                if (ii % 2) {
                    idx += w - 1;
                } else {
                    idx++;
                }
            }
        }
        ::glEnd();
    }

    delete[] pos;
    delete[] val;

    glDisable(GL_TEXTURE_1D);
    lmts = 0;
    glLightModeliv(GL_LIGHT_MODEL_TWO_SIDE, &lmts);

    return true;
}
<commit_msg>Volume slice plane now consists of triangle fans to emulate bi-linear interpolation because i am too lazy to implement the shader usage code<commit_after>/*
 * RenderVolumeSlice.cpp
 *
 * Copyright (C) 2012 by Universitaet Stuttgart (VISUS).
 * Alle Rechte vorbehalten.
 */

#include "stdafx.h"
#include "RenderVolumeSlice.h"
#include "CallVolumeData.h"
#include "param/StringParam.h"
#include "param/FloatParam.h"
#include <climits>
#include <cfloat>
#include <cmath>
#include "CoreInstance.h"
#include "view/CallClipPlane.h"
#include "view/CallGetTransferFunction.h"
#include "view/CallRender3D.h"
#include <GL/gl.h>
#include "vislib/assert.h"

using namespace megamol::core;

/*
 * RenderVolumeSlice::RenderVolumeSlice
 */
RenderVolumeSlice::RenderVolumeSlice(void) : Renderer3DModule(),
        getVolSlot("getVol", "The call for data"),
        getTFSlot("gettransferfunction", "The call for Transfer function"),
        getClipPlaneSlot("getclipplane", "The call for clipping plane"),
        attributeSlot("attr", "The attribute to show"),
        lowValSlot("low", "The low value"),
        highValSlot("high", "The high value") {

    this->getVolSlot.SetCompatibleCall<CallVolumeDataDescription>();
    this->MakeSlotAvailable(&this->getVolSlot);

    this->getTFSlot.SetCompatibleCall<view::CallGetTransferFunctionDescription>();
    this->MakeSlotAvailable(&this->getTFSlot);

    this->getClipPlaneSlot.SetCompatibleCall<view::CallClipPlaneDescription>();
    this->MakeSlotAvailable(&this->getClipPlaneSlot);

    this->attributeSlot << new param::StringParam("0");
    this->MakeSlotAvailable(&this->attributeSlot);

    this->lowValSlot << new param::FloatParam(0.0f);
    this->MakeSlotAvailable(&this->lowValSlot);

    this->highValSlot << new param::FloatParam(1.0f);
    this->MakeSlotAvailable(&this->highValSlot);

}

/*
 * RenderVolumeSlice::RenderVolumeSlice
 */
RenderVolumeSlice::~RenderVolumeSlice(void) {
    this->Release();
}

/*
 * RenderVolumeSlice::RenderVolumeSlice
 */
bool RenderVolumeSlice::create(void) {
    // intentionally empty
    return true;
}

/*
 * RenderVolumeSlice::RenderVolumeSlice
 */
bool RenderVolumeSlice::GetCapabilities(Call& call) {
    view::CallRender3D *cr = dynamic_cast<view::CallRender3D*>(&call);
    if (cr == NULL) return false;

    cr->SetCapabilities(
        view::CallRender3D::CAP_RENDER
        //| view::CallRender3D::CAP_LIGHTING
        | view::CallRender3D::CAP_ANIMATION
        );

    return true;
}

/*
 * RenderVolumeSlice::RenderVolumeSlice
 */
bool RenderVolumeSlice::GetExtents(Call& call) {
    view::CallRender3D *cr = dynamic_cast<view::CallRender3D*>(&call);
    if (cr == NULL) return false;

    CallVolumeData *c2 = this->getVolSlot.CallAs<CallVolumeData>();
    if ((c2 != NULL) && ((*c2)(1))) {
        cr->SetTimeFramesCount(c2->FrameCount());
        cr->AccessBoundingBoxes() = c2->AccessBoundingBoxes();
        cr->AccessBoundingBoxes().MakeScaledWorld(1.0f);

    } else {
        cr->SetTimeFramesCount(1);
        cr->AccessBoundingBoxes().Clear();
    }

    return true;
}

/*
 * RenderVolumeSlice::RenderVolumeSlice
 */
void RenderVolumeSlice::release(void) {
    // intentionally empty
}

/*
 * RenderVolumeSlice::RenderVolumeSlice
 */
bool RenderVolumeSlice::Render(Call& call) {
    view::CallRender3D *cr = dynamic_cast<view::CallRender3D*>(&call);
    if (cr == NULL) return false;

    // get volume data
    CallVolumeData *cvd = this->getVolSlot.CallAs<CallVolumeData>();
    if (cvd == NULL) return false;
    cvd->SetFrameID(static_cast<unsigned int>(cr->Time()));
    if (!(*cvd)(1)) return false;
    vislib::math::Cuboid<float> bbox = cvd->GetBoundingBoxes().ObjectSpaceBBox();
    cvd->SetFrameID(static_cast<unsigned int>(cr->Time()));
    if (!(*cvd)(0)) return false;

    float stepSize, ssy, ssz;
    stepSize = bbox.Width() / static_cast<float>(cvd->XSize());
    ssy = bbox.Height() / static_cast<float>(cvd->YSize());
    ssz = bbox.Depth() / static_cast<float>(cvd->ZSize());
    stepSize = fabs(stepSize + ssy + ssz) / 3.0f;

    // find the volumed attribute
    vislib::StringA attrName(this->attributeSlot.Param<param::StringParam>()->Value());
    unsigned int attrIdx = cvd->FindAttribute(attrName);
    if (attrIdx == UINT_MAX) {
        try {
            attrIdx = static_cast<unsigned int>(vislib::CharTraitsA::ParseInt(attrName));
        } catch(...) {
            return false;
        }
    }
    ASSERT(attrIdx != UINT_MAX);

    // get clip plane
    view::CallClipPlane *ccp = this->getClipPlaneSlot.CallAs<view::CallClipPlane>();
    if ((ccp == NULL) || (!(*ccp)())) return false;

    // get transfer function
    view::CallGetTransferFunction *cgtf = this->getTFSlot.CallAs<view::CallGetTransferFunction>();
    if ((cgtf == NULL) || (!(*cgtf)())) cgtf = NULL;

    unsigned int colTabSize;
    if (cgtf != NULL) {
        ::glDisable(GL_TEXTURE_2D);
        ::glEnable(GL_TEXTURE_1D);
        ::glEnable(GL_COLOR_MATERIAL);
        ::glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
        ::glBindTexture(GL_TEXTURE_1D, cgtf->OpenGLTexture());
        colTabSize = cgtf->TextureSize();
    } else {
        ::glDisable(GL_TEXTURE_1D);
        ::glBindTexture(GL_TEXTURE_1D, 0);
        colTabSize = 2;
    }

    float minVal = this->lowValSlot.Param<param::FloatParam>()->Value();
    float maxVal = this->highValSlot.Param<param::FloatParam>()->Value();

    vislib::math::Point<float, 3> ps[12];
    bool psv[12];

    {
        vislib::math::Plane<float> minX(bbox.GetLeftBottomBack(), vislib::math::Vector<float, 3>(-1.0f, 0.0f, 0.0f));
        vislib::math::Plane<float> minY(bbox.GetLeftBottomBack(), vislib::math::Vector<float, 3>(0.0f, -1.0f, 0.0f));
        vislib::math::Plane<float> minZ(bbox.GetLeftBottomBack(), vislib::math::Vector<float, 3>(0.0f, 0.0f, -1.0f));
        vislib::math::Plane<float> maxX(bbox.GetRightTopFront(), vislib::math::Vector<float, 3>(1.0f, 0.0f, 0.0f));
        vislib::math::Plane<float> maxY(bbox.GetRightTopFront(), vislib::math::Vector<float, 3>(0.0f, 1.0f, 0.0f));
        vislib::math::Plane<float> maxZ(bbox.GetRightTopFront(), vislib::math::Vector<float, 3>(0.0f, 0.0f, 1.0f));

        psv[0] = ccp->GetPlane().CalcIntersectionPoint(minX, minY, ps[0]);
        psv[1] = ccp->GetPlane().CalcIntersectionPoint(minX, minZ, ps[1]);
        psv[2] = ccp->GetPlane().CalcIntersectionPoint(minX, maxY, ps[2]);
        psv[3] = ccp->GetPlane().CalcIntersectionPoint(minX, maxZ, ps[3]);
        psv[4] = ccp->GetPlane().CalcIntersectionPoint(maxX, minY, ps[4]);
        psv[5] = ccp->GetPlane().CalcIntersectionPoint(maxX, minZ, ps[5]);
        psv[6] = ccp->GetPlane().CalcIntersectionPoint(maxX, maxY, ps[6]);
        psv[7] = ccp->GetPlane().CalcIntersectionPoint(maxX, maxZ, ps[7]);
        psv[8] = ccp->GetPlane().CalcIntersectionPoint(minY, minZ, ps[8]);
        psv[9] = ccp->GetPlane().CalcIntersectionPoint(minY, maxZ, ps[9]);
        psv[10] = ccp->GetPlane().CalcIntersectionPoint(maxY, minZ, ps[10]);
        psv[11] = ccp->GetPlane().CalcIntersectionPoint(maxY, maxZ, ps[11]);
    }

    unsigned int cnt = 0;
    vislib::math::Vector<float, 3> v;
    for (int i = 0; i < 12; i++) {
        if (!psv[i]) continue;
        v += vislib::math::Vector<float, 3>(ps[i]);
        cnt++;
    }
    if (cnt < 3) return false;
    v /= static_cast<float>(cnt);

    vislib::math::Point<float, 3> orig(v.PeekComponents());

    vislib::math::Vector<float, 3> x, y, z;
    ccp->CalcPlaneSystem(x, y, z);

    float minX, maxX, minY, maxY;
    minX = minY = 0.0f;
    maxX = maxY = 0.0f;
    for (int i = 0; i < 12; i++) {
        if (!psv[i]) continue;
        v = ps[i] - orig;
        float xc = v.Dot(x);
        float yc = v.Dot(y);
        if (minX > xc) minX = xc;
        if (maxX < xc) maxX = xc;
        if (minY > yc) minY = yc;
        if (maxY < yc) maxY = yc;
    }
    // loop ranges
    int minXi = static_cast<int>(minX / stepSize);
    int minYi = static_cast<int>(minY / stepSize);
    int maxXi = static_cast<int>(maxX / stepSize);
    int maxYi = static_cast<int>(maxY / stepSize);
    int w = (maxXi - minXi) + 1;
    int h = (maxYi - minYi) + 1;

    vislib::math::Vector<float, 3> *pos = new vislib::math::Vector<float, 3>[w * h];
    float *val = new float[w * h];

    for (int ix = minXi; ix <= maxXi; ix++) {
        int io = ix - minXi;
        for (int iy = minYi; iy <= maxYi; iy++) {
            int idx = (iy - minYi) * w + io;
            pos[idx] = orig;
            v = x;
            v *= static_cast<float>(ix) * stepSize;
            pos[idx] += v;
            v = y;
            v *= static_cast<float>(iy) * stepSize;
            pos[idx] += v;

            int vix = static_cast<int>(0.5f + (pos[idx].X() - bbox.Left()) * static_cast<float>(cvd->XSize()) / bbox.Width());
            int viy = static_cast<int>(0.5f + (pos[idx].Y() - bbox.Bottom()) * static_cast<float>(cvd->YSize()) / bbox.Height());
            int viz = static_cast<int>(0.5f + (pos[idx].Z() - bbox.Back()) * static_cast<float>(cvd->ZSize()) / bbox.Depth());
            if (vix < 0) vix = 0;
            if (vix >= static_cast<int>(cvd->XSize())) vix = cvd->XSize() - 1;
            if (viy < 0) viy = 0;
            if (viy >= static_cast<int>(cvd->YSize())) viy = cvd->YSize() - 1;
            if (viz < 0) viz = 0;
            if (viz >= static_cast<int>(cvd->ZSize())) viz = cvd->ZSize() - 1;

            val[idx] = cvd->Attribute(attrIdx).Floats()[vix + cvd->XSize() * (viy + cvd->YSize() * viz)];
            val[idx] = (val[idx] - minVal) / (maxVal - minVal);
            if (val[idx] < 0.0f) val[idx] = 0.0f;
            if (val[idx] > 1.0f) val[idx] = 1.0f;
        }
    }

    ::glNormal3fv(z.PeekComponents());

    ::glDisable(GL_CULL_FACE);
    GLint lmts = 1;
    ::glLightModeliv(GL_LIGHT_MODEL_TWO_SIDE, &lmts);
    ::glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    ::glColor3ub(255, 255, 255);
    ::glTexCoord1f(0.0f);

    int idx[4];
    for (int ix = minXi; ix < maxXi; ix++) {
        //::glBegin(GL_QUAD_STRIP);
        for (int iy = minYi; iy < maxYi; iy++) {
            idx[0] = (ix - minXi) + w * (iy - minYi);
            idx[1] = idx[0] + 1;
            idx[2] = idx[1] + w;
            idx[3] = idx[0] + w;

            float vf = (val[idx[0]] + val[idx[1]] + val[idx[2]] + val[idx[3]]) * 0.25f;
            v = pos[idx[0]];
            v += pos[idx[1]];
            v += pos[idx[2]];
            v += pos[idx[3]];
            v *= 0.25f;

            ::glBegin(GL_TRIANGLE_FAN);
            if (cgtf == NULL) ::glColor3f(vf, vf, vf); else ::glTexCoord1f(vf);
            ::glVertex3fv(v.PeekComponents());
            for (int ii = 0; ii <= 4; ii++) {
                vf = val[idx[ii % 4]];
                if (cgtf == NULL) ::glColor3f(vf, vf, vf); else ::glTexCoord1f(vf);
                ::glVertex3fv(pos[idx[ii % 4]].PeekComponents());
            }
            ::glEnd();

/*            for (int ii = 0; ii < 4; ii++) {
                if (cgtf == NULL) {
                    ::glColor3f(val[idx], val[idx], val[idx]);
                } else {
                    ::glTexCoord1f(val[idx]);
                }
                ::glVertex3fv(pos[idx].PeekComponents());
                if (ii % 2) {
                    idx += w - 1;
                } else {
                    idx++;
                }
            }*/
        }
        //::glEnd();
    }

    delete[] pos;
    delete[] val;

    glDisable(GL_TEXTURE_1D);
    lmts = 0;
    glLightModeliv(GL_LIGHT_MODEL_TWO_SIDE, &lmts);

    return true;
}
<|endoftext|>


<commit_before>// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/sync/glue/sync_start_util.h"

#include "base/bind.h"
#include "base/files/file_path.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "content/public/browser/browser_thread.h"

namespace {

void StartSyncOnUIThread(const base::FilePath& profile,
                         syncer::ModelType type) {
  ProfileManager* profile_manager = g_browser_process->profile_manager();
  Profile* p = profile_manager->GetProfileByPath(profile);
  if (!p) {
    DVLOG(2) << "Profile not found, can't start sync.";
    return;
  }

  ProfileSyncService* service = ProfileSyncServiceFactory::GetForProfile(p);
  if (!service) {
    DVLOG(2) << "No ProfileSyncService for profile, can't start sync.";
    return;
  }
  service->OnDataTypeRequestsSyncStartup(type);
}

void StartSyncProxy(const base::FilePath& profile,
                    syncer::ModelType type) {
  content::BrowserThread::PostTask(
      content::BrowserThread::UI, FROM_HERE,
      base::Bind(&StartSyncOnUIThread, profile, type));
}

}  // namespace

namespace sync_start_util {

syncer::SyncableService::StartSyncFlare GetFlareForSyncableService(
    const base::FilePath& profile_path) {
  return base::Bind(&StartSyncProxy, profile_path);
}

}  // namespace sync_start_util
<commit_msg>sync: null-check ProfileManager in sync_start_util<commit_after>// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/sync/glue/sync_start_util.h"

#include "base/bind.h"
#include "base/files/file_path.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "content/public/browser/browser_thread.h"

namespace {

void StartSyncOnUIThread(const base::FilePath& profile,
                         syncer::ModelType type) {
  ProfileManager* profile_manager = g_browser_process->profile_manager();
  if (!profile_manager) {
    // Can happen in tests.
    DVLOG(2) << "No ProfileManager, can't start sync.";
    return;
  }

  Profile* p = profile_manager->GetProfileByPath(profile);
  if (!p) {
    DVLOG(2) << "Profile not found, can't start sync.";
    return;
  }

  ProfileSyncService* service = ProfileSyncServiceFactory::GetForProfile(p);
  if (!service) {
    DVLOG(2) << "No ProfileSyncService for profile, can't start sync.";
    return;
  }
  service->OnDataTypeRequestsSyncStartup(type);
}

void StartSyncProxy(const base::FilePath& profile,
                    syncer::ModelType type) {
  content::BrowserThread::PostTask(
      content::BrowserThread::UI, FROM_HERE,
      base::Bind(&StartSyncOnUIThread, profile, type));
}

}  // namespace

namespace sync_start_util {

syncer::SyncableService::StartSyncFlare GetFlareForSyncableService(
    const base::FilePath& profile_path) {
  return base::Bind(&StartSyncProxy, profile_path);
}

}  // namespace sync_start_util
<|endoftext|>


<commit_before>// XMergeFilter.cpp: implementation of the CXMergeFilter class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"

#include "XMergeFilter.h"

#include <string>

#define ERR_NOJAVA       1
#define ERR_BADCLASSPATH 2
#define ERR_INITJAVA     3

const LPTSTR CXMergeFilter::m_pszPSWExportCLSID     = _T("{BDD611C3-7BAB-460F-8711-5B9AC9EF6020}");
const LPTSTR CXMergeFilter::m_pszPSWExportExt       = _T("sxw");
const LPTSTR CXMergeFilter::m_pszPSWExportDesc      = _T("OpenOffice.org Writer XML Document");
const LPTSTR CXMergeFilter::m_pszPSWExportShortDesc = _T("OpenOffice.org Writer");

const LPTSTR CXMergeFilter::m_pszPSWImportCLSID     = _T("{CB43F086-838D-4FA4-B5F6-3406B9A57439}");
const LPTSTR CXMergeFilter::m_pszPSWImportExt       = _T("psw");
const LPTSTR CXMergeFilter::m_pszPSWImportDesc      = _T("Pocket Word Document - Pocket PC");
const LPTSTR CXMergeFilter::m_pszPSWImportShortDesc = _T("Pocket Word");

const LPTSTR CXMergeFilter::m_pszPXLExportCLSID     = _T("{C6AB3E74-9F4F-4370-8120-A8A6FABB7A7C}");
const LPTSTR CXMergeFilter::m_pszPXLExportExt       = _T("sxc");
const LPTSTR CXMergeFilter::m_pszPXLExportDesc      = _T("OpenOffice.org Calc XML Document");
const LPTSTR CXMergeFilter::m_pszPXLExportShortDesc = _T("OpenOffice.org Calc");

const LPTSTR CXMergeFilter::m_pszPXLImportCLSID     = _T("{43887C67-4D5D-4127-BAAC-87A288494C7C}");
const LPTSTR CXMergeFilter::m_pszPXLImportExt       = _T("pxl");
const LPTSTR CXMergeFilter::m_pszPXLImportDesc      = _T("Pocket Excel Document - Pocket PC");
const LPTSTR CXMergeFilter::m_pszPXLImportShortDesc = _T("Pocket Excel");

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CXMergeFilter::CXMergeFilter() : m_cRef(1)
{
    m_bHaveExcel = FALSE;
    m_bHaveWord  = FALSE;

    m_szClasspath   = NULL;
    m_szJavaBaseDir = NULL;
}

CXMergeFilter::~CXMergeFilter()
{
    if (m_szClasspath != NULL)
    {
        delete m_szClasspath;
    }

    if (m_szJavaBaseDir != NULL)
    {
        delete m_szJavaBaseDir;
    }

}

//////////////////////////////////////////////////////////////////////
// IUnknown Methods
//////////////////////////////////////////////////////////////////////

STDMETHODIMP CXMergeFilter::QueryInterface(REFIID riid, void **ppvObject)
{
    if(ppvObject == NULL)
        return E_INVALIDARG;

    if (::IsEqualIID(riid, IID_IUnknown))
    {
        *ppvObject = static_cast<IUnknown *>(this);
    }
    else if (::IsEqualIID(riid, IID_ICeFileFilter))
    {
        *ppvObject = static_cast<ICeFileFilter *>(this);
    }
    else
    {
        *ppvObject = NULL;
        return E_NOINTERFACE;
    }

    reinterpret_cast<IUnknown *>(*ppvObject)->AddRef();
    return S_OK;
}

STDMETHODIMP_(ULONG) CXMergeFilter::AddRef()
{
    return ::InterlockedIncrement(&m_cRef);
}

STDMETHODIMP_(ULONG) CXMergeFilter::Release()
{
    if(::InterlockedDecrement(&m_cRef) == 0)
    {
        delete this;
        return 0;
    }
    return m_cRef;
}

//////////////////////////////////////////////////////////////////////
// ICeFileFilter
//////////////////////////////////////////////////////////////////////

STDMETHODIMP CXMergeFilter::FilterOptions(HWND hwndParent)
{
    // We don't currently allow any options
    return HRESULT_FROM_WIN32(NOERROR);
}

STDMETHODIMP CXMergeFilter::FormatMessage(DWORD dwFlags, DWORD dwMessageId,
                        DWORD dwLanguageId, LPTSTR lpBuffer, DWORD nSize,
                        va_list *Arguments, DWORD *pcb)
{
    char errMsg[50];

    switch(dwMessageId)
    {
    case ERR_NOJAVA:
        lstrcpy(errMsg, "Unable to locate Java 1.4 installation.");
        break;

    case ERR_BADCLASSPATH:
        lstrcpy(errMsg, "Unable to locate XMerge Jar files.");
        break;

    case ERR_INITJAVA:
        lstrcpy(errMsg, "Error initialising Java Runtime Environment.");
        break;
    }

    char* buf = (char*)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, strlen(errMsg));
    lstrcpy(buf, errMsg);

    *(char**)lpBuffer = buf;
    *pcb = strlen(errMsg);

    return HRESULT_FROM_WIN32(NOERROR);
}

STDMETHODIMP CXMergeFilter::NextConvertFile(int nConversion, CFF_CONVERTINFO *pci,
                             CFF_SOURCEFILE *psf, CFF_DESTINATIONFILE *pdf,
                             volatile BOOL *pbCancel, CF_ERROR *perr)
{
    std::string appArgs;
    std::string appName;

    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    ZeroMemory( &si, sizeof(si) );
    ZeroMemory( &pi, sizeof(pi) );

    si.cb = sizeof(si);

    /*
     * First step: Locate Java and establish the classpath.  If these can't
     *             be done succesfully, then avoid all further processing.
     */

    // Locate Java Home if it hasn't already been done.
    if (m_szJavaBaseDir == NULL)
    {
        m_szJavaBaseDir = GetJavaBaseDir();

        if (m_szJavaBaseDir == NULL)
        {
            *perr = ERR_NOJAVA;
            return HRESULT_FROM_WIN32(E_FAIL);
        }
    }

    // Get the StarOffice/OpenOffice class directory
    if (m_szClasspath == NULL)
    {
        m_szClasspath = GetXMergeClassPath();

        if (m_szClasspath == NULL)
        {
            *perr = ERR_BADCLASSPATH;
            return HRESULT_FROM_WIN32(E_FAIL);
        }
    }

    /*
     * Second step:  Check the files we're going to process.  If we don't have
     *               an XMerge plugin for the file then we can't convert.
     */
    if ((!lstrcmp(psf->szExtension, "sxw")  || !lstrcmp(psf->szExtension, "psw"))
            && !m_bHaveWord)
    {
        *perr = ERR_BADCLASSPATH;
        return HRESULT_FROM_WIN32(E_FAIL);
    }
    else if ((!lstrcmp(psf->szExtension, "sxc")  || !lstrcmp(psf->szExtension, "pxl"))
                 && !m_bHaveExcel)
    {
        *perr = ERR_BADCLASSPATH;
        return HRESULT_FROM_WIN32(E_FAIL);
    }

    /*
     * Third step:  Locate the Java executable and build and execute the command
     *              line to carry out the conversion.
     */

    // Find the Java executable and make sure it exists
    appName += m_szJavaBaseDir;
    appName += "\\bin\\javaw.exe";

    if (GetFileAttributes(appName.c_str()) == INVALID_FILE_SIZE)
    {
        *perr = ERR_NOJAVA;
        return HRESULT_FROM_WIN32(E_FAIL);
    }

    // Wrap the executable path in quotes in case of spaces
    appName.insert(0, "\"");
    appName.append("\"");

    // Need to build the entire command line for calling out to Java
    appArgs =  appName + " -Djava.class.path=";
    appArgs += m_szClasspath;
    appArgs += " org.openoffice.xmerge.util.ActiveSyncDriver ";

    if (!lstrcmp(psf->szExtension, "sxw"))
    {
        appArgs += "staroffice/sxw ";
        appArgs += "application/x-pocket-word ";
    }
    else if(!lstrcmp(psf->szExtension, "psw"))
    {
        appArgs += "application/x-pocket-word ";
        appArgs += "staroffice/sxw ";
    }
    else if(!lstrcmp(psf->szExtension, "sxc"))
    {
        appArgs += "staroffice/sxc ";
        appArgs += "application/x-pocket-excel ";
    }
    else if(!lstrcmp(psf->szExtension, "pxl"))
    {
        appArgs += "application/x-pocket-excel ";
        appArgs += "staroffice/sxc ";
    }

    // ActiveSync sometimes gives out long file names, especially when automatically syncing
    appArgs += "\"";
    appArgs += psf->szFullpath;
    appArgs += "\" \"";
    appArgs += pdf->szFullpath;
    appArgs += "\"";

    if(!CreateProcess(NULL,
                  (char*)appArgs.c_str(),
                  NULL,             // No Process Attributes
                  NULL,             // No Thread Attributes
                  FALSE,            // Don't want this process getting handles
                  CREATE_NO_WINDOW, // No console
                  NULL,             // No special environment
                  NULL,             // Current Working Directory is okay
                  &si,
                  &pi))
    {
        *perr = ERR_INITJAVA;
        return HRESULT_FROM_WIN32(E_FAIL);
    }

    // Wait for the new process to work
    WaitForSingleObject(pi.hProcess, INFINITE);

    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

    return HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS);
}

typedef HRESULT (WINAPI *SHGETFOLDERPATH)( HWND, int, HANDLE, DWORD, LPTSTR );

TCHAR* CXMergeFilter::GetJavaBaseDir()
{
    HRESULT lRet;

    HKEY hKey = NULL;
    HKEY hDataKey = NULL;

    TCHAR szClassName[_MAX_PATH] = "\0";
    TCHAR szKeyName[_MAX_PATH]   = "\0";
    DWORD dwClassName            = _MAX_PATH;
    DWORD dwKeyName              = _MAX_PATH;

    /*
     * Java leaves registry keys at HKLM\SOFTWARE\JavaSoft.
     *
     * Check for a JRE installation first
     */
    lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\JavaSoft\\Java Runtime Environment"), 0, KEY_READ, &hKey);
    if (lRet != ERROR_SUCCESS)
        return NULL;

    /*
     * Need to enumerate the subkeys.  The current version may not be 1.4 if
     * multiple versions are installed on the system.
     */
    for (DWORD i = 0; lRet != ERROR_NO_MORE_ITEMS; i++)
    {
        lRet = ::RegEnumKeyEx(hKey, i, szKeyName, &dwKeyName, 0, szClassName, &dwClassName, NULL);
        if(!strncmp(szKeyName, "1.4", 3))
            break;
    }

    // Found a Java 1.4 installation.  Can now read its home directory.
    lRet = ::RegOpenKeyEx(hKey, _T(szKeyName), 0, KEY_READ, &hDataKey);
    if (lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return NULL;
    }

    // Location shouldn't be greater than _MAX_PATH
    TCHAR*  szJavaHome = new TCHAR[_MAX_PATH + 1];
    DWORD dwSize = _MAX_PATH + 1;

    // Now read the JavaHome value
    lRet = ::RegQueryValueEx(hDataKey, _T("JavaHome"), 0, NULL, (LPBYTE)szJavaHome, &dwSize);
    if (lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hDataKey);
        RegCloseKey(hKey);
        delete szJavaHome;
        return NULL;
    }

    RegCloseKey(hDataKey);
    RegCloseKey(hKey);

    // Check that the directory exists before returning it
    DWORD dwAttrs = GetFileAttributes(szJavaHome);

    if (((dwAttrs & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY) || dwAttrs == INVALID_FILE_SIZE)
    {
        delete szJavaHome;
        return NULL;
    }

    return szJavaHome;
}

TCHAR* CXMergeFilter::GetXMergeClassPath()
{
    /*
     * The DLL will be installed by setup in the program directory of
     * the installation.  The XMerge Jar files, if present, will be
     * located in the classes directory below program.
     */

    TCHAR szJarPath[MAX_PATH];
    TCHAR szTmpPath[MAX_PATH];

    ZeroMemory(szJarPath, MAX_PATH);
    ZeroMemory(szTmpPath, MAX_PATH);

    WIN32_FILE_ATTRIBUTE_DATA fInfo;

    std::string clsPath;

    // Get the location of the module.
    GetModuleFileName(_Module.m_hInst, szTmpPath, MAX_PATH);

    // Strip off the xmergesync.dll component
    char* modName = strstr(szTmpPath, "xmergesync.dll");
    strncpy(szJarPath, szTmpPath, modName - szTmpPath);

    // Append the classes directory
    strncat(szJarPath, "classes\\", 8);

    // The core xmerge.jar must be present
    ZeroMemory(szTmpPath, MAX_PATH);
    _snprintf(szTmpPath, MAX_PATH, "%s%s\0", szJarPath, "xmerge.jar");

    if (!GetFileAttributesEx(szTmpPath, GetFileExInfoStandard, &fInfo))
    {
        return NULL;
    }
    else
    {
        clsPath += szTmpPath;
        clsPath += ";";
    }

    // Now check for Pocket Word
    ZeroMemory(szTmpPath, MAX_PATH);
    _snprintf(szTmpPath, MAX_PATH, "%s%s\0", szJarPath, "pocketword.jar");

    if (!GetFileAttributesEx(szTmpPath, GetFileExInfoStandard, &fInfo))
    {
        m_bHaveWord = FALSE;
    }
    else
    {
        m_bHaveWord = TRUE;
        clsPath += szTmpPath;
        clsPath += ";";
    }

    // Now check for Pocket Excel
    ZeroMemory(szTmpPath, MAX_PATH);
    _snprintf(szTmpPath, MAX_PATH, "%s%s\0", szJarPath, "pexcel.jar");

    if (!GetFileAttributesEx(szTmpPath, GetFileExInfoStandard, &fInfo))
    {
        m_bHaveExcel = FALSE;
    }
    else
    {
        m_bHaveExcel = TRUE;
        clsPath += szTmpPath;
        clsPath += ";";
    }

    // Quotes may be need around the ClassPath
    clsPath.insert(0, "\"");
    clsPath += "\"";

    // Return the data
    return _strdup(clsPath.c_str());
}
<commit_msg>#104344# Fix incorrect buffer-size being reported to RegEnumKeyEx<commit_after>// XMergeFilter.cpp: implementation of the CXMergeFilter class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"

#include "XMergeFilter.h"

#include <string>

#define ERR_NOJAVA       1
#define ERR_BADCLASSPATH 2
#define ERR_INITJAVA     3

const LPTSTR CXMergeFilter::m_pszPSWExportCLSID     = _T("{BDD611C3-7BAB-460F-8711-5B9AC9EF6020}");
const LPTSTR CXMergeFilter::m_pszPSWExportExt       = _T("sxw");
const LPTSTR CXMergeFilter::m_pszPSWExportDesc      = _T("OpenOffice.org Writer XML Document");
const LPTSTR CXMergeFilter::m_pszPSWExportShortDesc = _T("OpenOffice.org Writer");

const LPTSTR CXMergeFilter::m_pszPSWImportCLSID     = _T("{CB43F086-838D-4FA4-B5F6-3406B9A57439}");
const LPTSTR CXMergeFilter::m_pszPSWImportExt       = _T("psw");
const LPTSTR CXMergeFilter::m_pszPSWImportDesc      = _T("Pocket Word Document - Pocket PC");
const LPTSTR CXMergeFilter::m_pszPSWImportShortDesc = _T("Pocket Word");

const LPTSTR CXMergeFilter::m_pszPXLExportCLSID     = _T("{C6AB3E74-9F4F-4370-8120-A8A6FABB7A7C}");
const LPTSTR CXMergeFilter::m_pszPXLExportExt       = _T("sxc");
const LPTSTR CXMergeFilter::m_pszPXLExportDesc      = _T("OpenOffice.org Calc XML Document");
const LPTSTR CXMergeFilter::m_pszPXLExportShortDesc = _T("OpenOffice.org Calc");

const LPTSTR CXMergeFilter::m_pszPXLImportCLSID     = _T("{43887C67-4D5D-4127-BAAC-87A288494C7C}");
const LPTSTR CXMergeFilter::m_pszPXLImportExt       = _T("pxl");
const LPTSTR CXMergeFilter::m_pszPXLImportDesc      = _T("Pocket Excel Document - Pocket PC");
const LPTSTR CXMergeFilter::m_pszPXLImportShortDesc = _T("Pocket Excel");

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CXMergeFilter::CXMergeFilter() : m_cRef(1)
{
    m_bHaveExcel = FALSE;
    m_bHaveWord  = FALSE;

    m_szClasspath   = NULL;
    m_szJavaBaseDir = NULL;
}

CXMergeFilter::~CXMergeFilter()
{
    if (m_szClasspath != NULL)
    {
        delete m_szClasspath;
    }

    if (m_szJavaBaseDir != NULL)
    {
        delete m_szJavaBaseDir;
    }

}

//////////////////////////////////////////////////////////////////////
// IUnknown Methods
//////////////////////////////////////////////////////////////////////

STDMETHODIMP CXMergeFilter::QueryInterface(REFIID riid, void **ppvObject)
{
    if(ppvObject == NULL)
        return E_INVALIDARG;

    if (::IsEqualIID(riid, IID_IUnknown))
    {
        *ppvObject = static_cast<IUnknown *>(this);
    }
    else if (::IsEqualIID(riid, IID_ICeFileFilter))
    {
        *ppvObject = static_cast<ICeFileFilter *>(this);
    }
    else
    {
        *ppvObject = NULL;
        return E_NOINTERFACE;
    }

    reinterpret_cast<IUnknown *>(*ppvObject)->AddRef();
    return S_OK;
}

STDMETHODIMP_(ULONG) CXMergeFilter::AddRef()
{
    return ::InterlockedIncrement(&m_cRef);
}

STDMETHODIMP_(ULONG) CXMergeFilter::Release()
{
    if(::InterlockedDecrement(&m_cRef) == 0)
    {
        delete this;
        return 0;
    }
    return m_cRef;
}

//////////////////////////////////////////////////////////////////////
// ICeFileFilter
//////////////////////////////////////////////////////////////////////

STDMETHODIMP CXMergeFilter::FilterOptions(HWND hwndParent)
{
    // We don't currently allow any options
    return HRESULT_FROM_WIN32(NOERROR);
}

STDMETHODIMP CXMergeFilter::FormatMessage(DWORD dwFlags, DWORD dwMessageId,
                        DWORD dwLanguageId, LPTSTR lpBuffer, DWORD nSize,
                        va_list *Arguments, DWORD *pcb)
{
    char errMsg[50];

    switch(dwMessageId)
    {
    case ERR_NOJAVA:
        lstrcpy(errMsg, "Unable to locate Java 1.4 installation.");
        break;

    case ERR_BADCLASSPATH:
        lstrcpy(errMsg, "Unable to locate XMerge Jar files.");
        break;

    case ERR_INITJAVA:
        lstrcpy(errMsg, "Error initialising Java Runtime Environment.");
        break;
    }

    char* buf = (char*)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, strlen(errMsg));
    lstrcpy(buf, errMsg);

    *(char**)lpBuffer = buf;
    *pcb = strlen(errMsg);

    return HRESULT_FROM_WIN32(NOERROR);
}

STDMETHODIMP CXMergeFilter::NextConvertFile(int nConversion, CFF_CONVERTINFO *pci,
                             CFF_SOURCEFILE *psf, CFF_DESTINATIONFILE *pdf,
                             volatile BOOL *pbCancel, CF_ERROR *perr)
{
    std::string appArgs;
    std::string appName;

    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    ZeroMemory( &si, sizeof(si) );
    ZeroMemory( &pi, sizeof(pi) );

    si.cb = sizeof(si);

    /*
     * First step: Locate Java and establish the classpath.  If these can't
     *             be done succesfully, then avoid all further processing.
     */

    // Locate Java Home if it hasn't already been done.
    if (m_szJavaBaseDir == NULL)
    {
        m_szJavaBaseDir = GetJavaBaseDir();

        if (m_szJavaBaseDir == NULL)
        {
            *perr = ERR_NOJAVA;
            return HRESULT_FROM_WIN32(E_FAIL);
        }
    }

    // Get the StarOffice/OpenOffice class directory
    if (m_szClasspath == NULL)
    {
        m_szClasspath = GetXMergeClassPath();

        if (m_szClasspath == NULL)
        {
            *perr = ERR_BADCLASSPATH;
            return HRESULT_FROM_WIN32(E_FAIL);
        }
    }

    /*
     * Second step:  Check the files we're going to process.  If we don't have
     *               an XMerge plugin for the file then we can't convert.
     */
    if ((!lstrcmp(psf->szExtension, "sxw")  || !lstrcmp(psf->szExtension, "psw"))
            && !m_bHaveWord)
    {
        *perr = ERR_BADCLASSPATH;
        return HRESULT_FROM_WIN32(E_FAIL);
    }
    else if ((!lstrcmp(psf->szExtension, "sxc")  || !lstrcmp(psf->szExtension, "pxl"))
                 && !m_bHaveExcel)
    {
        *perr = ERR_BADCLASSPATH;
        return HRESULT_FROM_WIN32(E_FAIL);
    }

    /*
     * Third step:  Locate the Java executable and build and execute the command
     *              line to carry out the conversion.
     */

    // Find the Java executable and make sure it exists
    appName += m_szJavaBaseDir;
    appName += "\\bin\\javaw.exe";

    if (GetFileAttributes(appName.c_str()) == INVALID_FILE_SIZE)
    {
        *perr = ERR_NOJAVA;
        return HRESULT_FROM_WIN32(E_FAIL);
    }

    // Wrap the executable path in quotes in case of spaces
    appName.insert(0, "\"");
    appName.append("\"");

    // Need to build the entire command line for calling out to Java
    appArgs =  appName + " -Djava.class.path=";
    appArgs += m_szClasspath;
    appArgs += " org.openoffice.xmerge.util.ActiveSyncDriver ";

    if (!lstrcmp(psf->szExtension, "sxw"))
    {
        appArgs += "staroffice/sxw ";
        appArgs += "application/x-pocket-word ";
    }
    else if(!lstrcmp(psf->szExtension, "psw"))
    {
        appArgs += "application/x-pocket-word ";
        appArgs += "staroffice/sxw ";
    }
    else if(!lstrcmp(psf->szExtension, "sxc"))
    {
        appArgs += "staroffice/sxc ";
        appArgs += "application/x-pocket-excel ";
    }
    else if(!lstrcmp(psf->szExtension, "pxl"))
    {
        appArgs += "application/x-pocket-excel ";
        appArgs += "staroffice/sxc ";
    }

    // ActiveSync sometimes gives out long file names, especially when automatically syncing
    appArgs += "\"";
    appArgs += psf->szFullpath;
    appArgs += "\" \"";
    appArgs += pdf->szFullpath;
    appArgs += "\"";

    if(!CreateProcess(NULL,
                  (char*)appArgs.c_str(),
                  NULL,             // No Process Attributes
                  NULL,             // No Thread Attributes
                  FALSE,            // Don't want this process getting handles
                  CREATE_NO_WINDOW, // No console
                  NULL,             // No special environment
                  NULL,             // Current Working Directory is okay
                  &si,
                  &pi))
    {
        *perr = ERR_INITJAVA;
        return HRESULT_FROM_WIN32(E_FAIL);
    }

    // Wait for the new process to work
    WaitForSingleObject(pi.hProcess, INFINITE);

    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

    return HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS);
}

typedef HRESULT (WINAPI *SHGETFOLDERPATH)( HWND, int, HANDLE, DWORD, LPTSTR );

TCHAR* CXMergeFilter::GetJavaBaseDir()
{
    HRESULT lRet;

    HKEY hKey = NULL;
    HKEY hDataKey = NULL;

    TCHAR szClassName[_MAX_PATH] = "\0";
    TCHAR szKeyName[_MAX_PATH]   = "\0";
    DWORD dwClassName            = _MAX_PATH;
    DWORD dwKeyName              = _MAX_PATH;

    /*
     * Java leaves registry keys at HKLM\SOFTWARE\JavaSoft.
     *
     * Check for a JRE installation first
     */
    lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\JavaSoft\\Java Runtime Environment"), 0, KEY_READ, &hKey);
    if (lRet != ERROR_SUCCESS)
        return NULL;

    /*
     * Need to enumerate the subkeys.  The current version may not be 1.4 if
     * multiple versions are installed on the system.
     */
    for (DWORD i = 0; lRet != ERROR_NO_MORE_ITEMS; i++)
    {
        lRet = ::RegEnumKeyEx(hKey, i, szKeyName, &dwKeyName, 0, szClassName, &dwClassName, NULL);
        if(!strncmp(szKeyName, "1.4", 3))
            break;
        dwKeyName = _MAX_PATH;
    }

    // Found a Java 1.4 installation.  Can now read its home directory.
    lRet = ::RegOpenKeyEx(hKey, _T(szKeyName), 0, KEY_READ, &hDataKey);
    if (lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return NULL;
    }

    // Location shouldn't be greater than _MAX_PATH
    TCHAR*  szJavaHome = new TCHAR[_MAX_PATH + 1];
    DWORD dwSize = _MAX_PATH + 1;

    // Now read the JavaHome value
    lRet = ::RegQueryValueEx(hDataKey, _T("JavaHome"), 0, NULL, (LPBYTE)szJavaHome, &dwSize);
    if (lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hDataKey);
        RegCloseKey(hKey);
        delete szJavaHome;
        return NULL;
    }

    RegCloseKey(hDataKey);
    RegCloseKey(hKey);

    // Check that the directory exists before returning it
    DWORD dwAttrs = GetFileAttributes(szJavaHome);

    if (((dwAttrs & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY) || dwAttrs == INVALID_FILE_SIZE)
    {
        delete szJavaHome;
        return NULL;
    }

    return szJavaHome;
}

TCHAR* CXMergeFilter::GetXMergeClassPath()
{
    /*
     * The DLL will be installed by setup in the program directory of
     * the installation.  The XMerge Jar files, if present, will be
     * located in the classes directory below program.
     */

    TCHAR szJarPath[MAX_PATH];
    TCHAR szTmpPath[MAX_PATH];

    ZeroMemory(szJarPath, MAX_PATH);
    ZeroMemory(szTmpPath, MAX_PATH);

    WIN32_FILE_ATTRIBUTE_DATA fInfo;

    std::string clsPath;

    // Get the location of the module.
    GetModuleFileName(_Module.m_hInst, szTmpPath, MAX_PATH);

    // Strip off the xmergesync.dll component
    char* modName = strstr(szTmpPath, "xmergesync.dll");
    strncpy(szJarPath, szTmpPath, modName - szTmpPath);

    // Append the classes directory
    strncat(szJarPath, "classes\\", 8);

    // The core xmerge.jar must be present
    ZeroMemory(szTmpPath, MAX_PATH);
    _snprintf(szTmpPath, MAX_PATH, "%s%s\0", szJarPath, "xmerge.jar");

    if (!GetFileAttributesEx(szTmpPath, GetFileExInfoStandard, &fInfo))
    {
        return NULL;
    }
    else
    {
        clsPath += szTmpPath;
        clsPath += ";";
    }

    // Now check for Pocket Word
    ZeroMemory(szTmpPath, MAX_PATH);
    _snprintf(szTmpPath, MAX_PATH, "%s%s\0", szJarPath, "pocketword.jar");

    if (!GetFileAttributesEx(szTmpPath, GetFileExInfoStandard, &fInfo))
    {
        m_bHaveWord = FALSE;
    }
    else
    {
        m_bHaveWord = TRUE;
        clsPath += szTmpPath;
        clsPath += ";";
    }

    // Now check for Pocket Excel
    ZeroMemory(szTmpPath, MAX_PATH);
    _snprintf(szTmpPath, MAX_PATH, "%s%s\0", szJarPath, "pexcel.jar");

    if (!GetFileAttributesEx(szTmpPath, GetFileExInfoStandard, &fInfo))
    {
        m_bHaveExcel = FALSE;
    }
    else
    {
        m_bHaveExcel = TRUE;
        clsPath += szTmpPath;
        clsPath += ";";
    }

    // Quotes may be need around the ClassPath
    clsPath.insert(0, "\"");
    clsPath += "\"";

    // Return the data
    return _strdup(clsPath.c_str());
}
<|endoftext|>


<commit_before>/*
 * SIFFWriter.cpp
 *
 * Copyright (C) 2013 by TU Dresden
 * Alle Rechte vorbehalten.
 */

#include "stdafx.h"
#include "io/SIFFWriter.h"
#include "mmcore/BoundingBoxes.h"
#include "mmcore/param/FilePathParam.h"
#include "mmcore/param/BoolParam.h"
#include "mmcore/param/EnumParam.h"
#include "vislib/sys/Log.h"
#include "vislib/sys/MemmappedFile.h"
#include "vislib/String.h"
#include "vislib/sys/Thread.h"
#include "vislib/sys/sysfunctions.h"

using namespace megamol;
using namespace megamol::stdplugin::moldyn::io;

/*
 * SIFFWriter::SIFFWriter
 */
SIFFWriter::SIFFWriter(void) : AbstractDataWriter(),
        filenameSlot("filename", "The path to the MMPLD file to be written"),
        asciiSlot("ascii", "Set to true to write ASCII-versions of SIFF"),
        versionSlot("version", "The file format version to write"),
        dataSlot("data", "The slot requesting the data to be written") {

    this->filenameSlot << new core::param::FilePathParam("");
    this->MakeSlotAvailable(&this->filenameSlot);

    this->asciiSlot << new core::param::BoolParam(false);
    this->MakeSlotAvailable(&this->asciiSlot);

    core::param::EnumParam *version = new core::param::EnumParam(100);
    version->SetTypePair(100, "1.0");
    version->SetTypePair(101, "1.1");
    this->versionSlot << version;
    this->MakeSlotAvailable(&this->versionSlot);

    this->dataSlot.SetCompatibleCall<core::moldyn::MultiParticleDataCallDescription>();
    this->MakeSlotAvailable(&this->dataSlot);
}

/*
 * SIFFWriter::~SIFFWriter
 */
SIFFWriter::~SIFFWriter(void) {
    this->Release();
}

/*
 * SIFFWriter::create
 */
bool SIFFWriter::create(void) {
    // intentionally empty
    return true;
}

/*
 * SIFFWriter::release
 */
void SIFFWriter::release(void) {
    // intentionally empty
}

/*
 * SIFFWriter::run
 */
bool SIFFWriter::run(void) {
    using vislib::sys::Log;
    using vislib::Exception;

    vislib::TString filename(this->filenameSlot.Param<core::param::FilePathParam>()->Value());
    if (filename.IsEmpty()) {
        Log::DefaultLog.WriteMsg(Log::LEVEL_ERROR, "No file name specified. Abort.");
        return false;
    }

    core::moldyn::MultiParticleDataCall *mpdc = this->dataSlot.CallAs<core::moldyn::MultiParticleDataCall>();
    if (mpdc == NULL) {
        Log::DefaultLog.WriteMsg(Log::LEVEL_ERROR, "No data source connected. Abort.");
        return false;
    }

    bool useAscii = this->asciiSlot.Param<core::param::BoolParam>()->Value();

    int version = this->versionSlot.Param<core::param::EnumParam>()->Value();
    if ((version != 100) && (version != 101)) {
        Log::DefaultLog.WriteMsg(Log::LEVEL_ERROR, "Unsupported version requested. Abort.");
        return false;
    }

    if (vislib::sys::File::Exists(filename)) {
        Log::DefaultLog.WriteMsg(Log::LEVEL_WARN, "File %s already exists and will be overwritten.", vislib::StringA(filename).PeekBuffer());
    }

    mpdc->SetFrameID(0);
    if (!(*mpdc)(0)) {
        Log::DefaultLog.WriteMsg(Log::LEVEL_ERROR, "Could not get data (Frame 0). Abort.");
        return false;
    }

    vislib::sys::MemmappedFile file;
    if (!file.Open(filename, vislib::sys::File::WRITE_ONLY, vislib::sys::File::SHARE_EXCLUSIVE, vislib::sys::File::CREATE_OVERWRITE)) {
        Log::DefaultLog.WriteMsg(Log::LEVEL_ERROR, "Unable to create output file \"%s\". Abort.", vislib::StringA(filename).PeekBuffer());
        return false;
    }

    try {

        if (file.Write("SIFF", 4) != 4) throw Exception("Write failed", __FILE__, __LINE__);
        if (file.Write((useAscii ? "a" : "b"), 1) != 1) throw Exception("Write failed", __FILE__, __LINE__);
        if (useAscii) {
            vislib::sys::WriteFormattedLineToFile(file, "%u.%u\n", version / 100, version % 100);
        } else {
            unsigned short v = static_cast<unsigned short>(version);
            if (file.Write(&v, 2) != 2) throw Exception("Write failed", __FILE__, __LINE__);
        }

        for (unsigned int i = 0; i < mpdc->GetParticleListCount(); i++) {
            core::moldyn::MultiParticleDataCall::Particles &parts = mpdc->AccessParticles(i);
            const char *colPtr = static_cast<const char*>(parts.GetColourData());
            const char *vertPtr = static_cast<const char*>(parts.GetVertexData());
            size_t colStep = 0;
            size_t vertStep = 0;
            unsigned char col[3];
            bool colFloats = false;
            bool singleCol = false;
            bool hasRad = false;
            float minC = 0.0f, maxC = 0.0f;

            // colour
            switch (parts.GetColourDataType()) {
                case core::moldyn::MultiParticleDataCall::Particles::COLDATA_NONE:
                    ::memcpy(col, parts.GetGlobalColour(), 3);
                    colPtr = NULL;
                    break;
                case core::moldyn::MultiParticleDataCall::Particles::COLDATA_UINT8_RGB:
                    colStep = 3;
                    break;
                case core::moldyn::MultiParticleDataCall::Particles::COLDATA_UINT8_RGBA:
                    colStep = 4;
                    break;
                case core::moldyn::MultiParticleDataCall::Particles::COLDATA_FLOAT_RGB:
                    colStep = 12;
                    colFloats = true;
                    break;
                case core::moldyn::MultiParticleDataCall::Particles::COLDATA_FLOAT_RGBA:
                    colStep = 16;
                    colFloats = true;
                    break;
                case core::moldyn::MultiParticleDataCall::Particles::COLDATA_FLOAT_I:
                    colStep = 4;
                    colFloats = true;
                    singleCol = true;
                    minC = parts.GetMinColourIndexValue();
                    maxC = parts.GetMaxColourIndexValue();
                    break;
                default:
                    col[0] = col[1] = col[2] = 127;
                    colPtr = NULL;
                    break;
            }
            colStep = vislib::math::Max<size_t>(colStep, parts.GetColourDataStride());

            // radius and position
            switch (parts.GetVertexDataType()) {
                case core::moldyn::MultiParticleDataCall::Particles::VERTDATA_NONE:
                    continue;
                case core::moldyn::MultiParticleDataCall::Particles::VERTDATA_FLOAT_XYZ:
                    vertStep = 12;
                    ASSERT(vertPtr != NULL);
                    break;
                case core::moldyn::MultiParticleDataCall::Particles::VERTDATA_FLOAT_XYZR:
                    vertStep = 16;
                    hasRad = true;
                    ASSERT(vertPtr != NULL);
                    break;
                default:
                    continue;
            }
            vertStep = vislib::math::Max<size_t>(vertStep, parts.GetVertexDataStride());

            for (UINT64 i = 0, cnt = parts.GetCount(); i < cnt; ++i) {

                if (colPtr != NULL) {
                    if (colFloats) {
                        float c[3];
                        if (singleCol) {
                            c[0] = c[1] = c[2] = (*reinterpret_cast<const float*>(colPtr) - minC) / (maxC - minC);
                        } else {
                            ::memcpy(c, colPtr, 3 * sizeof(float));
                        }
                        for (int j = 0; j < 3; j++) {
                            col[j] = static_cast<unsigned char>(vislib::math::Clamp<int>(static_cast<int>(c[j] * 255.0f), 0, 255));
                        }
                    } else {
                        ::memcpy(col, colPtr, 3);
                    }
                    colPtr += colStep;
                }

                float v[4];
                v[3] = parts.GetGlobalRadius();
                ::memcpy(v, vertPtr, (hasRad ? 4 : 3) * sizeof(float));
                vertPtr += vertStep;

                if (useAscii) {
                    vislib::sys::WriteFormattedLineToFile(file,
                        (version == 101)
                        ? "%f %f %f\n"
                        : "%f %f %f %f %u %u %u\n",
                        v[0], v[1], v[2], v[3], col[0], col[1], col[2]);
                } else {
                    if (version == 101) {
                        if (file.Write(&v, 12) != 12) throw Exception("Write failed", __FILE__, __LINE__);
                    } else if (version == 100) {
                        if (file.Write(&v, 16) != 16) throw Exception("Write failed", __FILE__, __LINE__);
                        if (file.Write(&col, 3) != 3) throw Exception("Write failed", __FILE__, __LINE__);
                    }
                }

            }
        }

        Log::DefaultLog.WriteMsg(Log::LEVEL_INFO, "Completed writing data\n");
    } catch(Exception ex) {
        Log::DefaultLog.WriteError("Failed to write: %s (%s, %d)\n", ex.GetMsgA(), ex.GetFile(), ex.GetLine());
    } catch(...) {
        Log::DefaultLog.WriteError("Failed to write: unexpected exception\n");
    }
    file.Close();

    return true;
}

/*
 * SIFFWriter::getCapabilities
 */
bool SIFFWriter::getCapabilities(core::DataWriterCtrlCall& call) {
    call.SetAbortable(false);
    return true;
}
<commit_msg>Forcing frame 0<commit_after>/*
 * SIFFWriter.cpp
 *
 * Copyright (C) 2013 by TU Dresden
 * Alle Rechte vorbehalten.
 */

#include "stdafx.h"
#include "io/SIFFWriter.h"
#include "mmcore/BoundingBoxes.h"
#include "mmcore/param/FilePathParam.h"
#include "mmcore/param/BoolParam.h"
#include "mmcore/param/EnumParam.h"
#include "vislib/sys/Log.h"
#include "vislib/sys/MemmappedFile.h"
#include "vislib/String.h"
#include "vislib/sys/Thread.h"
#include "vislib/sys/sysfunctions.h"

using namespace megamol;
using namespace megamol::stdplugin::moldyn::io;

/*
 * SIFFWriter::SIFFWriter
 */
SIFFWriter::SIFFWriter(void) : AbstractDataWriter(),
        filenameSlot("filename", "The path to the MMPLD file to be written"),
        asciiSlot("ascii", "Set to true to write ASCII-versions of SIFF"),
        versionSlot("version", "The file format version to write"),
        dataSlot("data", "The slot requesting the data to be written") {

    this->filenameSlot << new core::param::FilePathParam("");
    this->MakeSlotAvailable(&this->filenameSlot);

    this->asciiSlot << new core::param::BoolParam(false);
    this->MakeSlotAvailable(&this->asciiSlot);

    core::param::EnumParam *version = new core::param::EnumParam(100);
    version->SetTypePair(100, "1.0");
    version->SetTypePair(101, "1.1");
    this->versionSlot << version;
    this->MakeSlotAvailable(&this->versionSlot);

    this->dataSlot.SetCompatibleCall<core::moldyn::MultiParticleDataCallDescription>();
    this->MakeSlotAvailable(&this->dataSlot);
}

/*
 * SIFFWriter::~SIFFWriter
 */
SIFFWriter::~SIFFWriter(void) {
    this->Release();
}

/*
 * SIFFWriter::create
 */
bool SIFFWriter::create(void) {
    // intentionally empty
    return true;
}

/*
 * SIFFWriter::release
 */
void SIFFWriter::release(void) {
    // intentionally empty
}

/*
 * SIFFWriter::run
 */
bool SIFFWriter::run(void) {
    using vislib::sys::Log;
    using vislib::Exception;

    vislib::TString filename(this->filenameSlot.Param<core::param::FilePathParam>()->Value());
    if (filename.IsEmpty()) {
        Log::DefaultLog.WriteMsg(Log::LEVEL_ERROR, "No file name specified. Abort.");
        return false;
    }

    core::moldyn::MultiParticleDataCall *mpdc = this->dataSlot.CallAs<core::moldyn::MultiParticleDataCall>();
    if (mpdc == NULL) {
        Log::DefaultLog.WriteMsg(Log::LEVEL_ERROR, "No data source connected. Abort.");
        return false;
    }

    bool useAscii = this->asciiSlot.Param<core::param::BoolParam>()->Value();

    int version = this->versionSlot.Param<core::param::EnumParam>()->Value();
    if ((version != 100) && (version != 101)) {
        Log::DefaultLog.WriteMsg(Log::LEVEL_ERROR, "Unsupported version requested. Abort.");
        return false;
    }

    if (vislib::sys::File::Exists(filename)) {
        Log::DefaultLog.WriteMsg(Log::LEVEL_WARN, "File %s already exists and will be overwritten.", vislib::StringA(filename).PeekBuffer());
    }

    mpdc->SetFrameID(0, true);
    if (!(*mpdc)(0)) {
        Log::DefaultLog.WriteMsg(Log::LEVEL_ERROR, "Could not get data (Frame 0). Abort.");
        return false;
    }

    vislib::sys::MemmappedFile file;
    if (!file.Open(filename, vislib::sys::File::WRITE_ONLY, vislib::sys::File::SHARE_EXCLUSIVE, vislib::sys::File::CREATE_OVERWRITE)) {
        Log::DefaultLog.WriteMsg(Log::LEVEL_ERROR, "Unable to create output file \"%s\". Abort.", vislib::StringA(filename).PeekBuffer());
        return false;
    }

    try {

        if (file.Write("SIFF", 4) != 4) throw Exception("Write failed", __FILE__, __LINE__);
        if (file.Write((useAscii ? "a" : "b"), 1) != 1) throw Exception("Write failed", __FILE__, __LINE__);
        if (useAscii) {
            vislib::sys::WriteFormattedLineToFile(file, "%u.%u\n", version / 100, version % 100);
        } else {
            unsigned short v = static_cast<unsigned short>(version);
            if (file.Write(&v, 2) != 2) throw Exception("Write failed", __FILE__, __LINE__);
        }

        for (unsigned int i = 0; i < mpdc->GetParticleListCount(); i++) {
            core::moldyn::MultiParticleDataCall::Particles &parts = mpdc->AccessParticles(i);
            const char *colPtr = static_cast<const char*>(parts.GetColourData());
            const char *vertPtr = static_cast<const char*>(parts.GetVertexData());
            size_t colStep = 0;
            size_t vertStep = 0;
            unsigned char col[3];
            bool colFloats = false;
            bool singleCol = false;
            bool hasRad = false;
            float minC = 0.0f, maxC = 0.0f;

            // colour
            switch (parts.GetColourDataType()) {
                case core::moldyn::MultiParticleDataCall::Particles::COLDATA_NONE:
                    ::memcpy(col, parts.GetGlobalColour(), 3);
                    colPtr = NULL;
                    break;
                case core::moldyn::MultiParticleDataCall::Particles::COLDATA_UINT8_RGB:
                    colStep = 3;
                    break;
                case core::moldyn::MultiParticleDataCall::Particles::COLDATA_UINT8_RGBA:
                    colStep = 4;
                    break;
                case core::moldyn::MultiParticleDataCall::Particles::COLDATA_FLOAT_RGB:
                    colStep = 12;
                    colFloats = true;
                    break;
                case core::moldyn::MultiParticleDataCall::Particles::COLDATA_FLOAT_RGBA:
                    colStep = 16;
                    colFloats = true;
                    break;
                case core::moldyn::MultiParticleDataCall::Particles::COLDATA_FLOAT_I:
                    colStep = 4;
                    colFloats = true;
                    singleCol = true;
                    minC = parts.GetMinColourIndexValue();
                    maxC = parts.GetMaxColourIndexValue();
                    break;
                default:
                    col[0] = col[1] = col[2] = 127;
                    colPtr = NULL;
                    break;
            }
            colStep = vislib::math::Max<size_t>(colStep, parts.GetColourDataStride());

            // radius and position
            switch (parts.GetVertexDataType()) {
                case core::moldyn::MultiParticleDataCall::Particles::VERTDATA_NONE:
                    continue;
                case core::moldyn::MultiParticleDataCall::Particles::VERTDATA_FLOAT_XYZ:
                    vertStep = 12;
                    ASSERT(vertPtr != NULL);
                    break;
                case core::moldyn::MultiParticleDataCall::Particles::VERTDATA_FLOAT_XYZR:
                    vertStep = 16;
                    hasRad = true;
                    ASSERT(vertPtr != NULL);
                    break;
                default:
                    continue;
            }
            vertStep = vislib::math::Max<size_t>(vertStep, parts.GetVertexDataStride());

            for (UINT64 i = 0, cnt = parts.GetCount(); i < cnt; ++i) {

                if (colPtr != NULL) {
                    if (colFloats) {
                        float c[3];
                        if (singleCol) {
                            c[0] = c[1] = c[2] = (*reinterpret_cast<const float*>(colPtr) - minC) / (maxC - minC);
                        } else {
                            ::memcpy(c, colPtr, 3 * sizeof(float));
                        }
                        for (int j = 0; j < 3; j++) {
                            col[j] = static_cast<unsigned char>(vislib::math::Clamp<int>(static_cast<int>(c[j] * 255.0f), 0, 255));
                        }
                    } else {
                        ::memcpy(col, colPtr, 3);
                    }
                    colPtr += colStep;
                }

                float v[4];
                v[3] = parts.GetGlobalRadius();
                ::memcpy(v, vertPtr, (hasRad ? 4 : 3) * sizeof(float));
                vertPtr += vertStep;

                if (useAscii) {
                    vislib::sys::WriteFormattedLineToFile(file,
                        (version == 101)
                        ? "%f %f %f\n"
                        : "%f %f %f %f %u %u %u\n",
                        v[0], v[1], v[2], v[3], col[0], col[1], col[2]);
                } else {
                    if (version == 101) {
                        if (file.Write(&v, 12) != 12) throw Exception("Write failed", __FILE__, __LINE__);
                    } else if (version == 100) {
                        if (file.Write(&v, 16) != 16) throw Exception("Write failed", __FILE__, __LINE__);
                        if (file.Write(&col, 3) != 3) throw Exception("Write failed", __FILE__, __LINE__);
                    }
                }

            }
        }

        Log::DefaultLog.WriteMsg(Log::LEVEL_INFO, "Completed writing data\n");
    } catch(Exception ex) {
        Log::DefaultLog.WriteError("Failed to write: %s (%s, %d)\n", ex.GetMsgA(), ex.GetFile(), ex.GetLine());
    } catch(...) {
        Log::DefaultLog.WriteError("Failed to write: unexpected exception\n");
    }
    file.Close();

    return true;
}

/*
 * SIFFWriter::getCapabilities
 */
bool SIFFWriter::getCapabilities(core::DataWriterCtrlCall& call) {
    call.SetAbortable(false);
    return true;
}
<|endoftext|>