<commit_before>/*
    Copyright (C) 2013  David Edmundson <davidedmundson@kde.org>

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

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

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

#include "imdetailswidget.h"

#include <QGridLayout>
#include <QLabel>
#include <QApplication>
#include <QStyle>
#include <QVBoxLayout>

#include <KLocalizedString>
#include <KPluginFactory>

#include <KPeople/PersonData>
#include <TelepathyQt/AccountManager>
#include <KTp/core.h>

K_PLUGIN_FACTORY_WITH_JSON( ImDetailsWidgetFactory, "imdetailswidgetplugin.json", registerPlugin<ImDetailsWidget>(); )
K_EXPORT_PLUGIN( ImDetailsWidgetFactory("imdetailswidgetplugin", "ktp-common-internals"))

using namespace KPeople;

ImDetailsWidget::ImDetailsWidget(QObject* parent, const QVariantList& args)
{
    Q_UNUSED(parent);
    Q_UNUSED(args);
}

QString ImDetailsWidget::label() const
{
    return i18n("IM");
}

QWidget* ImDetailsWidget::createDetailsWidget(const KPeople::PersonData &person, QWidget *parent) const
{
    QWidget *root = new QWidget(parent);
    QGridLayout *layout = new QGridLayout(root);
    root->setLayout(layout);

    int row = 0;
    for(const QString &contactId: person.contactUris()) {
        PersonData contact(contactId);
        const QString tpcontactId = contact.contactCustomProperty(QStringLiteral("telepathy-contactId")).toString();
        const QString accountPath = contact.contactCustomProperty(QStringLiteral("telepathy-accountPath")).toString(); //probably unused till we fix everything properly

        Tp::AccountPtr account = KTp::accountManager()->accountForObjectPath(accountPath);
        if (!account) {
            continue;
        }

        QLabel *iconLabel = new QLabel(root);
        const int iconSize = root->style()->pixelMetric(QStyle::PM_SmallIconSize);
        iconLabel->setPixmap(QIcon::fromTheme(account->iconName()).pixmap(iconSize, iconSize));
        layout->addWidget(iconLabel, row, 0);

        QLabel *label = new QLabel(tpcontactId, root);
        label->setTextInteractionFlags(Qt::TextSelectableByMouse);
        layout->addWidget(label, row, 1);

        row++;
        //FUTURE - presence here + blocked + presence subscription
    }
    if (layout->count()) {
        return root;
    } else {
        return 0;
    }
}

#include "imdetailswidget.moc"
<commit_msg>Don't crash when working with non-KTp accounts<commit_after>/*
    Copyright (C) 2013  David Edmundson <davidedmundson@kde.org>

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

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

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

#include "imdetailswidget.h"

#include <QGridLayout>
#include <QLabel>
#include <QApplication>
#include <QStyle>
#include <QVBoxLayout>

#include <KLocalizedString>
#include <KPluginFactory>

#include <KPeople/PersonData>
#include <TelepathyQt/AccountManager>
#include <KTp/core.h>

K_PLUGIN_FACTORY_WITH_JSON( ImDetailsWidgetFactory, "imdetailswidgetplugin.json", registerPlugin<ImDetailsWidget>(); )
K_EXPORT_PLUGIN( ImDetailsWidgetFactory("imdetailswidgetplugin", "ktp-common-internals"))

using namespace KPeople;

ImDetailsWidget::ImDetailsWidget(QObject* parent, const QVariantList& args)
{
    Q_UNUSED(parent);
    Q_UNUSED(args);
}

QString ImDetailsWidget::label() const
{
    return i18n("IM");
}

QWidget* ImDetailsWidget::createDetailsWidget(const KPeople::PersonData &person, QWidget *parent) const
{
    QWidget *root = new QWidget(parent);
    QGridLayout *layout = new QGridLayout(root);
    root->setLayout(layout);

    int row = 0;
    for(const QString &contactId: person.contactUris()) {
        if (!contactId.startsWith("ktp://")) {
            continue;
        }
        PersonData contact(contactId);
        const QString tpcontactId = contact.contactCustomProperty(QStringLiteral("telepathy-contactId")).toString();
        const QString accountPath = contact.contactCustomProperty(QStringLiteral("telepathy-accountPath")).toString(); //probably unused till we fix everything properly

        Tp::AccountPtr account = KTp::accountManager()->accountForObjectPath(accountPath);
        if (!account) {
            continue;
        }

        QLabel *iconLabel = new QLabel(root);
        const int iconSize = root->style()->pixelMetric(QStyle::PM_SmallIconSize);
        iconLabel->setPixmap(QIcon::fromTheme(account->iconName()).pixmap(iconSize, iconSize));
        layout->addWidget(iconLabel, row, 0);

        QLabel *label = new QLabel(tpcontactId, root);
        label->setTextInteractionFlags(Qt::TextSelectableByMouse);
        layout->addWidget(label, row, 1);

        row++;
        //FUTURE - presence here + blocked + presence subscription
    }
    if (layout->count()) {
        return root;
    } else {
        return 0;
    }
}

#include "imdetailswidget.moc"
<|endoftext|>


<commit_before>/*
    This file is part of libkcal.

    Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>

    This library 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 library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

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

#include <typeinfo>
#include <stdlib.h>

#include <qdatetime.h>
#include <qstring.h>
#include <qptrlist.h>

#include <klocale.h>
#include <kdebug.h>
#include <kurl.h>
#include <kio/job.h>
#include <kstandarddirs.h>

#include <kabc/stdaddressbook.h>
#include <kabc/locknull.h>

#include "libkcal/vcaldrag.h"
#include "libkcal/vcalformat.h"
#include "libkcal/icalformat.h"
#include "libkcal/exceptions.h"
#include "libkcal/incidence.h"
#include "libkcal/event.h"
#include "libkcal/todo.h"
#include "libkcal/journal.h"
#include "libkcal/filestorage.h"
#include "libkcal/alarm.h"

#include <kresources/configwidget.h>

#include "resourcekabcconfig.h"

#include "resourcekabc.h"

using namespace KCal;

extern "C"
{
  void *init_kcal_kabc()
  {
	  KGlobal::locale()->insertCatalogue( "kres_birthday" );
    KGlobal::locale()->insertCatalogue( "libkcal" );
    return new KRES::PluginFactory<ResourceKABC,ResourceKABCConfig>();
  }
}

ResourceKABC::ResourceKABC( const KConfig* config )
  : ResourceCalendar( config ), mCalendar( QString::fromLatin1( "UTC" ) ),
    mAlarmDays( 0 ), mAlarm( true ), mUseCategories( false )
{
  if ( config ) {
    readConfig( config );
  }

  init();
}

ResourceKABC::ResourceKABC()
  : ResourceCalendar( 0 ), mCalendar( QString::fromLatin1( "UTC" ) ),
    mAlarmDays( 0 ), mAlarm( true ), mUseCategories( false )
{
  init();
}

ResourceKABC::~ResourceKABC()
{
  delete mLock;
}

void ResourceKABC::init()
{
  setType( "birthdays" );

  setReadOnly( true );

  mLock = new KABC::LockNull( false );

  mAddressbook = 0;
}

void ResourceKABC::readConfig( const KConfig *config )
{
  mAlarmDays = config->readNumEntry( "AlarmDays", 0 );
  mAlarm = config->readBoolEntry( "Alarm", true );
  mCategories = config->readListEntry( "Categories" );
  mUseCategories = config->readBoolEntry( "UseCategories", false );
}

void ResourceKABC::writeConfig( KConfig *config )
{
  ResourceCalendar::writeConfig( config );
  config->writeEntry( "AlarmDays", mAlarmDays );
  config->writeEntry( "Alarm", mAlarm );
  config->writeEntry( "Categories", mCategories );
  config->writeEntry( "UseCategories", mUseCategories );
}

bool ResourceKABC::doOpen()
{
  kdDebug(5800) << "ResourceKABC::doOpen()" << endl;

  mAddressbook = KABC::StdAddressBook::self( true );
  connect( mAddressbook, SIGNAL(addressBookChanged(AddressBook*)), SLOT( reload() ) );

  return true;
}

bool ResourceKABC::doLoad()
{
  kdDebug(5800) << "ResourceKABC::load()" << endl;

  mCalendar.close();

  // import from kabc
  QString summary;
  QStringList::ConstIterator strIt;
  const QStringList::ConstIterator endStrIt = mCategories.end();
  KABC::Addressee::List anniversaries;
  KABC::Addressee::List::Iterator addrIt;

  KABC::AddressBook::Iterator it;
  const KABC::AddressBook::Iterator endIt = mAddressbook->end();
  for ( it = mAddressbook->begin(); it != endIt; ++it ) {

    if ( mUseCategories ) {
      bool hasCategory = false;
      QStringList categories = (*it).categories();
      for ( strIt = mCategories.begin(); strIt != endStrIt; ++strIt )
        if ( categories.contains( *strIt ) ) {
          hasCategory = true;
          break;
        }

      if ( !hasCategory )
        continue;
    }

    QDate birthdate = (*it).birthday().date();
    QString name_1, email_1, uid_1;
    if ( birthdate.isValid() ) {
      kdDebug(5800) << "found a birthday " << birthdate.toString() << endl;

      name_1 = (*it).nickName();
      email_1 = (*it).fullEmail();
      uid_1 = (*it).uid();
      if (name_1.isEmpty()) name_1 = (*it).realName();
      summary = i18n("%1's birthday").arg( name_1 );

      Event *ev = new Event();
      ev->setUid( uid_1+"_KABC_Birthday");

      ev->setDtStart(birthdate);
      ev->setDtEnd(birthdate);
      ev->setHasEndDate(true);
      ev->setFloats(true);
      ev->setTransparency( Event::Transparent );

      ev->setCustomProperty("KABC", "BIRTHDAY", "YES");
      ev->setCustomProperty("KABC", "UID-1", uid_1 );
      ev->setCustomProperty("KABC", "NAME-1", name_1 );
      ev->setCustomProperty("KABC", "EMAIL-1", email_1 );
      kdDebug(5800) << "ResourceKABC::doLoad: uid:" << uid_1 << " name: " << name_1
        << " email: " << email_1 << endl;
      ev->setSummary(summary);

      // Set the recurrence
      Recurrence *vRecurrence = ev->recurrence();
      vRecurrence->setStartDate( birthdate );
      vRecurrence->setYearly( 1 );
      if ( birthdate.month()==2 && birthdate.day()==29 ) {
        vRecurrence->addYearlyDay( 60 );
      }

      ev->clearAlarms();

      if ( mAlarm ) {
        // Set the alarm
        Alarm* vAlarm = ev->newAlarm();
        vAlarm->setText(summary);
        vAlarm->setTime(birthdate);
        // 24 hours before. duration is in seconds.
        vAlarm->setStartOffset( -86400 * mAlarmDays );
        vAlarm->setEnabled(true);
      }

      // insert category
      ev->setCategories(i18n("Birthday"));

      ev->setReadOnly( true );
      mCalendar.addEvent(ev);
      kdDebug(5800) << "imported " << birthdate.toString() << endl;
    }

    QString anniversary_string = (*it).custom( "KADDRESSBOOK", "X-Anniversary" );
    if (anniversary_string.isEmpty() )
      continue;
    QDateTime anniversary = QDate::fromString( anniversary_string, Qt::ISODate );
    if ( !anniversary.isValid() )
      continue;

    QString name = (*it).custom( "KADDRESSBOOK", "X-SpousesName" );
    if ( name.isEmpty() )
      anniversaries.append( *it );
    else {
      bool found = false;
      for ( addrIt = anniversaries.begin(); addrIt != anniversaries.end(); ++addrIt ) {
        if ( name == (*addrIt).realName() ) {
          QDate spouseAnniversary =
            QDate::fromString( (*addrIt).custom( "KADDRESSBOOK", "X-Anniversary" ), Qt::ISODate );
          if ( anniversary == spouseAnniversary ) {
            found = true;
            break;

          }
        }
      }

      if ( !found )
        anniversaries.append( *it );
    }
  }

  for ( addrIt = anniversaries.begin(); addrIt != anniversaries.end(); ++addrIt ) {
    QDate anniversary = QDate::fromString( (*addrIt).custom( "KADDRESSBOOK", "X-Anniversary" ), Qt::ISODate );
    kdDebug(5800) << "found a anniversary " << anniversary.toString() << endl;
    QString name;
    QString name_1 = (*addrIt).nickName();
    QString uid_1 = (*addrIt).uid();
    QString email_1 = (*addrIt).fullEmail();
    if ( name_1.isEmpty() )
      name_1 = (*addrIt).realName();

    QString spouseName = (*addrIt).custom( "KADDRESSBOOK", "X-SpousesName" );
    QString name_2,email_2,uid_2;
    if ( !spouseName.isEmpty() ) {
      //TODO: find a KABC:Addressee of the spouse
      //      Probably easiest would be to use a QMap (as the spouse's entry was already searched above!
      KABC::Addressee spouse;
      spouse.setNameFromString( spouseName );
      uid_2 = spouse.uid();
      email_2 = spouse.fullEmail();
      name_2 = spouse.nickName();
      if ( name_2.isEmpty() ) {
        name_2 = spouse.givenName();
      }
      if ( name_2.isEmpty() ) {
        name_2 = spouseName;
      }
      summary = i18n("insert names of both spouses",
                     "%1's & %2's anniversary").arg( name_1 ).arg( name_2 );
    } else {
      summary = i18n("only one spouse in addressbook, insert the name",
                     "%1's anniversary").arg( name_1 );
    }

    Event *ev = new Event();
      ev->setUid( uid_1+"_KABC_Anniversary" );

    ev->setDtStart(anniversary);
    ev->setDtEnd(anniversary);
    ev->setHasEndDate(true);
    ev->setFloats(true);

    ev->setSummary(summary);

    ev->setCustomProperty( "KABC", "BIRTHDAY", "YES" );

    ev->setCustomProperty( "KABC", "UID-1", uid_1 );
    ev->setCustomProperty( "KABC", "NAME-1", name_1 );
    ev->setCustomProperty( "KABC", "EMAIL-1", email_1 );
    ev->setCustomProperty( "KABC", "ANNIVERSARY", "YES" );
    if ( !spouseName.isEmpty() ) {
      ev->setCustomProperty("KABC", "UID-2", uid_2 );
      ev->setCustomProperty("KABC", "NAME-2", spouseName );
      ev->setCustomProperty("KABC", "EMAIL-2", email_2 );
    }
    // Set the recurrence
    Recurrence *vRecurrence = ev->recurrence();
    vRecurrence->setStartDate( anniversary );
    vRecurrence->setYearly( 1 );
    if ( anniversary.month()==2 && anniversary.day()==29 ) {
      vRecurrence->addYearlyDay( 60 );
    }

    ev->clearAlarms();

    if ( mAlarm ) {
      // Set the alarm
      Alarm* vAlarm = ev->newAlarm();
      vAlarm->setText(summary);
      vAlarm->setTime(anniversary);
      // 24 hours before. duration is in seconds.
      vAlarm->setStartOffset( -86400 * mAlarmDays );
      vAlarm->setEnabled(true);
    }

    // insert category
    ev->setCategories(i18n("Anniversary"));

    ev->setReadOnly( true );
    mCalendar.addEvent(ev);
    kdDebug(5800) << "imported " << anniversary.toString() << endl;
  }

  return true;
}

void ResourceKABC::setAlarm( bool a )
{
  mAlarm = a;
}

bool ResourceKABC::alarm()
{
  return mAlarm;
}

void ResourceKABC::setAlarmDays( int ad )
{
  mAlarmDays = ad;
}

int ResourceKABC::alarmDays()
{
  return mAlarmDays;
}

void ResourceKABC::setCategories( const QStringList &categories )
{
  mCategories = categories;
}

QStringList ResourceKABC::categories() const
{
  return mCategories;
}

void ResourceKABC::setUseCategories( bool useCategories )
{
  mUseCategories = useCategories;
}

bool ResourceKABC::useCategories() const
{
  return mUseCategories;
}

bool ResourceKABC::doSave()
{
  // is always read only!
  return true;
}

bool ResourceKABC::isSaving()
{
  return false;
}

KABC::Lock *ResourceKABC::lock()
{
  return mLock;
}

bool ResourceKABC::addEvent(Event*)
{
  return false;
}

bool ResourceKABC::deleteEvent(Event*)
{
  return false;
}

Event *ResourceKABC::event( const QString &uid )
{
  return mCalendar.event( uid );
}

Event::List ResourceKABC::rawEventsForDate( const QDate &date,
                                             EventSortField sortField,
                                             SortDirection sortDirection )
{
  return mCalendar.rawEventsForDate( date, sortField, sortDirection );
}

Event::List ResourceKABC::rawEvents( const QDate &start, const QDate &end,
                                          bool inclusive )
{
  return mCalendar.rawEvents( start, end, inclusive );
}

Event::List ResourceKABC::rawEventsForDate(const QDateTime &qdt)
{
  return mCalendar.rawEventsForDate( qdt.date() );
}

Event::List ResourceKABC::rawEvents( EventSortField sortField, SortDirection sortDirection )
{
  return mCalendar.rawEvents( sortField, sortDirection );
}

bool ResourceKABC::addTodo(Todo*)
{
  return false;
}

bool ResourceKABC::deleteTodo(Todo*)
{
  return false;
}

Todo::List ResourceKABC::rawTodos( TodoSortField sortField, SortDirection sortDirection )
{
  return mCalendar.rawTodos( sortField, sortDirection );
}

Todo *ResourceKABC::todo( const QString &uid )
{
  return mCalendar.todo( uid );
}

Todo::List ResourceKABC::rawTodosForDate( const QDate &date )
{
  return mCalendar.rawTodosForDate( date );
}

bool ResourceKABC::addJournal(Journal*)
{
  return false;
}

bool ResourceKABC::deleteJournal(Journal*)
{
  return false;
}

Journal *ResourceKABC::journal(const QString &uid)
{
  return mCalendar.journal( uid );
}

Journal::List ResourceKABC::rawJournals( JournalSortField sortField, SortDirection sortDirection )
{
  return mCalendar.rawJournals( sortField, sortDirection );
}

Journal::List ResourceKABC::rawJournalsForDate( const QDate &date )
{
  return mCalendar.rawJournalsForDate( date );
}

Alarm::List ResourceKABC::alarmsTo( const QDateTime &to )
{
  return mCalendar.alarmsTo( to );
}

Alarm::List ResourceKABC::alarms( const QDateTime &from, const QDateTime &to )
{
//  kdDebug(5800) << "ResourceKABC::alarms(" << from.toString() << " - " << to.toString() << ")\n";

  return mCalendar.alarms( from, to );
}

void ResourceKABC::dump() const
{
  ResourceCalendar::dump();
}

void ResourceKABC::reload()
{
  load();
  emit resourceChanged( this );
}

void ResourceKABC::setTimeZoneId( const QString& tzid )
{
  mCalendar.setTimeZoneId( tzid );
}

#include "resourcekabc.moc"
<commit_msg>Be smarter about deriving the partner's name for the anniversary string to use in the Event summary. kolab/issue2974<commit_after>/*
    This file is part of libkcal.

    Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>

    This library 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 library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

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

#include <typeinfo>
#include <stdlib.h>

#include <qdatetime.h>
#include <qstring.h>
#include <qptrlist.h>

#include <klocale.h>
#include <kdebug.h>
#include <kurl.h>
#include <kio/job.h>
#include <kstandarddirs.h>

#include <kabc/stdaddressbook.h>
#include <kabc/locknull.h>

#include "libkcal/vcaldrag.h"
#include "libkcal/vcalformat.h"
#include "libkcal/icalformat.h"
#include "libkcal/exceptions.h"
#include "libkcal/incidence.h"
#include "libkcal/event.h"
#include "libkcal/todo.h"
#include "libkcal/journal.h"
#include "libkcal/filestorage.h"
#include "libkcal/alarm.h"

#include <libemailfunctions/email.h>

#include <kresources/configwidget.h>

#include "resourcekabcconfig.h"

#include "resourcekabc.h"

using namespace KCal;

extern "C"
{
  void *init_kcal_kabc()
  {
	  KGlobal::locale()->insertCatalogue( "kres_birthday" );
    KGlobal::locale()->insertCatalogue( "libkcal" );
    return new KRES::PluginFactory<ResourceKABC,ResourceKABCConfig>();
  }
}

ResourceKABC::ResourceKABC( const KConfig* config )
  : ResourceCalendar( config ), mCalendar( QString::fromLatin1( "UTC" ) ),
    mAlarmDays( 0 ), mAlarm( true ), mUseCategories( false )
{
  if ( config ) {
    readConfig( config );
  }

  init();
}

ResourceKABC::ResourceKABC()
  : ResourceCalendar( 0 ), mCalendar( QString::fromLatin1( "UTC" ) ),
    mAlarmDays( 0 ), mAlarm( true ), mUseCategories( false )
{
  init();
}

ResourceKABC::~ResourceKABC()
{
  delete mLock;
}

void ResourceKABC::init()
{
  setType( "birthdays" );

  setReadOnly( true );

  mLock = new KABC::LockNull( false );

  mAddressbook = 0;
}

void ResourceKABC::readConfig( const KConfig *config )
{
  mAlarmDays = config->readNumEntry( "AlarmDays", 0 );
  mAlarm = config->readBoolEntry( "Alarm", true );
  mCategories = config->readListEntry( "Categories" );
  mUseCategories = config->readBoolEntry( "UseCategories", false );
}

void ResourceKABC::writeConfig( KConfig *config )
{
  ResourceCalendar::writeConfig( config );
  config->writeEntry( "AlarmDays", mAlarmDays );
  config->writeEntry( "Alarm", mAlarm );
  config->writeEntry( "Categories", mCategories );
  config->writeEntry( "UseCategories", mUseCategories );
}

bool ResourceKABC::doOpen()
{
  kdDebug(5800) << "ResourceKABC::doOpen()" << endl;

  mAddressbook = KABC::StdAddressBook::self( true );
  connect( mAddressbook, SIGNAL(addressBookChanged(AddressBook*)), SLOT( reload() ) );

  return true;
}

bool ResourceKABC::doLoad()
{
  kdDebug(5800) << "ResourceKABC::load()" << endl;

  mCalendar.close();

  // import from kabc
  QString summary;
  QStringList::ConstIterator strIt;
  const QStringList::ConstIterator endStrIt = mCategories.end();
  KABC::Addressee::List anniversaries;
  KABC::Addressee::List::Iterator addrIt;

  KABC::AddressBook::Iterator it;
  const KABC::AddressBook::Iterator endIt = mAddressbook->end();
  for ( it = mAddressbook->begin(); it != endIt; ++it ) {

    if ( mUseCategories ) {
      bool hasCategory = false;
      QStringList categories = (*it).categories();
      for ( strIt = mCategories.begin(); strIt != endStrIt; ++strIt )
        if ( categories.contains( *strIt ) ) {
          hasCategory = true;
          break;
        }

      if ( !hasCategory )
        continue;
    }

    QDate birthdate = (*it).birthday().date();
    QString name_1, email_1, uid_1;
    if ( birthdate.isValid() ) {
      kdDebug(5800) << "found a birthday " << birthdate.toString() << endl;

      name_1 = (*it).nickName();
      email_1 = (*it).fullEmail();
      uid_1 = (*it).uid();
      if (name_1.isEmpty()) name_1 = (*it).realName();
      summary = i18n("%1's birthday").arg( name_1 );

      Event *ev = new Event();
      ev->setUid( uid_1+"_KABC_Birthday");

      ev->setDtStart(birthdate);
      ev->setDtEnd(birthdate);
      ev->setHasEndDate(true);
      ev->setFloats(true);
      ev->setTransparency( Event::Transparent );

      ev->setCustomProperty("KABC", "BIRTHDAY", "YES");
      ev->setCustomProperty("KABC", "UID-1", uid_1 );
      ev->setCustomProperty("KABC", "NAME-1", name_1 );
      ev->setCustomProperty("KABC", "EMAIL-1", email_1 );
      kdDebug(5800) << "ResourceKABC::doLoad: uid:" << uid_1 << " name: " << name_1
        << " email: " << email_1 << endl;
      ev->setSummary(summary);

      // Set the recurrence
      Recurrence *vRecurrence = ev->recurrence();
      vRecurrence->setStartDate( birthdate );
      vRecurrence->setYearly( 1 );
      if ( birthdate.month()==2 && birthdate.day()==29 ) {
        vRecurrence->addYearlyDay( 60 );
      }

      ev->clearAlarms();

      if ( mAlarm ) {
        // Set the alarm
        Alarm* vAlarm = ev->newAlarm();
        vAlarm->setText(summary);
        vAlarm->setTime(birthdate);
        // 24 hours before. duration is in seconds.
        vAlarm->setStartOffset( -86400 * mAlarmDays );
        vAlarm->setEnabled(true);
      }

      // insert category
      ev->setCategories(i18n("Birthday"));

      ev->setReadOnly( true );
      mCalendar.addEvent(ev);
      kdDebug(5800) << "imported " << birthdate.toString() << endl;
    }

    QString anniversary_string = (*it).custom( "KADDRESSBOOK", "X-Anniversary" );
    if (anniversary_string.isEmpty() )
      continue;
    QDateTime anniversary = QDate::fromString( anniversary_string, Qt::ISODate );
    if ( !anniversary.isValid() )
      continue;

    QString name = (*it).custom( "KADDRESSBOOK", "X-SpousesName" );
    if ( name.isEmpty() )
      anniversaries.append( *it );
    else {
      bool found = false;
      for ( addrIt = anniversaries.begin(); addrIt != anniversaries.end(); ++addrIt ) {
        if ( name == (*addrIt).realName() ) {
          QDate spouseAnniversary =
            QDate::fromString( (*addrIt).custom( "KADDRESSBOOK", "X-Anniversary" ), Qt::ISODate );
          if ( anniversary == spouseAnniversary ) {
            found = true;
            break;

          }
        }
      }

      if ( !found )
        anniversaries.append( *it );
    }
  }

  for ( addrIt = anniversaries.begin(); addrIt != anniversaries.end(); ++addrIt ) {
    QDate anniversary = QDate::fromString( (*addrIt).custom( "KADDRESSBOOK", "X-Anniversary" ), Qt::ISODate );
    kdDebug(5800) << "found a anniversary " << anniversary.toString() << endl;
    QString name;
    QString name_1 = (*addrIt).nickName();
    QString uid_1 = (*addrIt).uid();
    QString email_1 = (*addrIt).fullEmail();
    if ( name_1.isEmpty() )
      name_1 = (*addrIt).realName();

    QString spouseName = (*addrIt).custom( "KADDRESSBOOK", "X-SpousesName" );
    QString name_2,email_2,uid_2;
    if ( !spouseName.isEmpty() ) {
      QString tname, temail;
      KPIM::getNameAndMail( spouseName, tname, temail );
      tname = KPIM::quoteNameIfNecessary( tname );
      if ( ( tname[0] == '"' ) && ( tname[tname.length() - 1] == '"' ) ) {
        tname.remove( 0, 1 );
        tname.truncate( tname.length() - 1 );
      }
      KABC::Addressee spouse;
      spouse.setNameFromString( tname );
      name_2 = spouse.nickName();
      uid_2 = spouse.uid();
      email_2 = spouse.fullEmail();
      if ( name_2.isEmpty() ) {
        name_2 = spouse.realName();
      }
      summary = i18n("insert names of both spouses",
                     "%1's & %2's anniversary").arg( name_1 ).arg( tname );
    } else {
      summary = i18n("only one spouse in addressbook, insert the name",
                     "%1's anniversary").arg( name_1 );
    }

    Event *ev = new Event();
    ev->setUid( uid_1+"_KABC_Anniversary" );

    ev->setDtStart(anniversary);
    ev->setDtEnd(anniversary);
    ev->setHasEndDate(true);
    ev->setFloats(true);

    ev->setSummary(summary);

    ev->setCustomProperty( "KABC", "BIRTHDAY", "YES" );

    ev->setCustomProperty( "KABC", "UID-1", uid_1 );
    ev->setCustomProperty( "KABC", "NAME-1", name_1 );
    ev->setCustomProperty( "KABC", "EMAIL-1", email_1 );
    ev->setCustomProperty( "KABC", "ANNIVERSARY", "YES" );
    if ( !spouseName.isEmpty() ) {
      ev->setCustomProperty("KABC", "UID-2", uid_2 );
      ev->setCustomProperty("KABC", "NAME-2", spouseName );
      ev->setCustomProperty("KABC", "EMAIL-2", email_2 );
    }
    // Set the recurrence
    Recurrence *vRecurrence = ev->recurrence();
    vRecurrence->setStartDate( anniversary );
    vRecurrence->setYearly( 1 );
    if ( anniversary.month()==2 && anniversary.day()==29 ) {
      vRecurrence->addYearlyDay( 60 );
    }

    ev->clearAlarms();

    if ( mAlarm ) {
      // Set the alarm
      Alarm* vAlarm = ev->newAlarm();
      vAlarm->setText(summary);
      vAlarm->setTime(anniversary);
      // 24 hours before. duration is in seconds.
      vAlarm->setStartOffset( -86400 * mAlarmDays );
      vAlarm->setEnabled(true);
    }

    // insert category
    ev->setCategories(i18n("Anniversary"));

    ev->setReadOnly( true );
    mCalendar.addEvent(ev);
    kdDebug(5800) << "imported " << anniversary.toString() << endl;
  }

  return true;
}

void ResourceKABC::setAlarm( bool a )
{
  mAlarm = a;
}

bool ResourceKABC::alarm()
{
  return mAlarm;
}

void ResourceKABC::setAlarmDays( int ad )
{
  mAlarmDays = ad;
}

int ResourceKABC::alarmDays()
{
  return mAlarmDays;
}

void ResourceKABC::setCategories( const QStringList &categories )
{
  mCategories = categories;
}

QStringList ResourceKABC::categories() const
{
  return mCategories;
}

void ResourceKABC::setUseCategories( bool useCategories )
{
  mUseCategories = useCategories;
}

bool ResourceKABC::useCategories() const
{
  return mUseCategories;
}

bool ResourceKABC::doSave()
{
  // is always read only!
  return true;
}

bool ResourceKABC::isSaving()
{
  return false;
}

KABC::Lock *ResourceKABC::lock()
{
  return mLock;
}

bool ResourceKABC::addEvent(Event*)
{
  return false;
}

bool ResourceKABC::deleteEvent(Event*)
{
  return false;
}

Event *ResourceKABC::event( const QString &uid )
{
  return mCalendar.event( uid );
}

Event::List ResourceKABC::rawEventsForDate( const QDate &date,
                                             EventSortField sortField,
                                             SortDirection sortDirection )
{
  return mCalendar.rawEventsForDate( date, sortField, sortDirection );
}

Event::List ResourceKABC::rawEvents( const QDate &start, const QDate &end,
                                          bool inclusive )
{
  return mCalendar.rawEvents( start, end, inclusive );
}

Event::List ResourceKABC::rawEventsForDate(const QDateTime &qdt)
{
  return mCalendar.rawEventsForDate( qdt.date() );
}

Event::List ResourceKABC::rawEvents( EventSortField sortField, SortDirection sortDirection )
{
  return mCalendar.rawEvents( sortField, sortDirection );
}

bool ResourceKABC::addTodo(Todo*)
{
  return false;
}

bool ResourceKABC::deleteTodo(Todo*)
{
  return false;
}

Todo::List ResourceKABC::rawTodos( TodoSortField sortField, SortDirection sortDirection )
{
  return mCalendar.rawTodos( sortField, sortDirection );
}

Todo *ResourceKABC::todo( const QString &uid )
{
  return mCalendar.todo( uid );
}

Todo::List ResourceKABC::rawTodosForDate( const QDate &date )
{
  return mCalendar.rawTodosForDate( date );
}

bool ResourceKABC::addJournal(Journal*)
{
  return false;
}

bool ResourceKABC::deleteJournal(Journal*)
{
  return false;
}

Journal *ResourceKABC::journal(const QString &uid)
{
  return mCalendar.journal( uid );
}

Journal::List ResourceKABC::rawJournals( JournalSortField sortField, SortDirection sortDirection )
{
  return mCalendar.rawJournals( sortField, sortDirection );
}

Journal::List ResourceKABC::rawJournalsForDate( const QDate &date )
{
  return mCalendar.rawJournalsForDate( date );
}

Alarm::List ResourceKABC::alarmsTo( const QDateTime &to )
{
  return mCalendar.alarmsTo( to );
}

Alarm::List ResourceKABC::alarms( const QDateTime &from, const QDateTime &to )
{
//  kdDebug(5800) << "ResourceKABC::alarms(" << from.toString() << " - " << to.toString() << ")\n";

  return mCalendar.alarms( from, to );
}

void ResourceKABC::dump() const
{
  ResourceCalendar::dump();
}

void ResourceKABC::reload()
{
  load();
  emit resourceChanged( this );
}

void ResourceKABC::setTimeZoneId( const QString& tzid )
{
  mCalendar.setTimeZoneId( tzid );
}

#include "resourcekabc.moc"
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// OpenGL ES 2.0 code

#include <jni.h>
#include <android/log.h>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <string>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define  LOG_TAG    "libgl2jni"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

static void printGLString(const char *name, GLenum s) {
    const char *v = (const char *) glGetString(s);
    LOGI("GL %s = %s\n", name, v);
}

static void checkGlError(const char* op) {
    for (GLint error = glGetError(); error; error
            = glGetError()) {
        LOGI("after %s() glError (0x%x)\n", op, error);
    }
}

static std::string gVertexShader;
static std::string gFragmentShader;

static int android_read(void* cookie, char* buf, int size) {
    return AAsset_read((AAsset*)cookie, buf, size);
}

static int android_write(void* cookie, const char* buf, int size) {
    return EACCES; // can't provide write access to the apk
}

static fpos_t android_seek(void* cookie, fpos_t offset, int whence) {
    return AAsset_seek((AAsset*)cookie, offset, whence);
}

static int android_close(void* cookie) {
    AAsset_close((AAsset*)cookie);
    return 0;
}

FILE* android_fopen(const char* fname, const char* mode, AAssetManager *assetManager) {
    if(mode[0] == 'w') return NULL;

    AAsset* asset = AAssetManager_open( assetManager, fname, 0);
    if(!asset) return NULL;

    return funopen(asset, android_read, android_write, android_seek, android_close);
}

std::string readShaderToString( FILE* fileDescriptor ) {
    const unsigned N=1024;
    std::string total;
    while (true) {
        char buffer[ N ];
        size_t read = fread((void *)&buffer[0], 1, N, fileDescriptor);
        if (read) {
            for ( int c = 0; c <  read; ++c ) {
                total.push_back( buffer[ c ] );
            }
        }
        if (read < N) { break; }
    }

    return total;
}

void loadShaders( JNIEnv* env, jobject& obj ) {
    AAssetManager *asset_manager = AAssetManager_fromJava( env, obj );
    FILE* fd;
    fd = android_fopen( "vertex.glsl", "r", asset_manager );
    gVertexShader = readShaderToString( fd );
    fclose( fd );
    fd = android_fopen( "fragment.glsl", "r", asset_manager );
    gFragmentShader = readShaderToString( fd );
    fclose( fd );
}

GLuint loadShader(GLenum shaderType, const char* pSource) {
    GLuint shader = glCreateShader(shaderType);
    if (shader) {
        glShaderSource(shader, 1, &pSource, NULL);
        glCompileShader(shader);
        GLint compiled = 0;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
        if (!compiled) {
            GLint infoLen = 0;
            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
            if (infoLen) {
                char* buf = (char*) malloc(infoLen);
                if (buf) {
                    glGetShaderInfoLog(shader, infoLen, NULL, buf);
                    LOGE("Could not compile shader %d:\n%s\n",
                            shaderType, buf);
                    free(buf);
                }
                glDeleteShader(shader);
                shader = 0;
            }
        }
    }
    return shader;
}

GLuint createProgram(const char* pVertexSource, const char* pFragmentSource) {
    GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource);
    if (!vertexShader) {
        return 0;
    }

    GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource);
    if (!pixelShader) {
        return 0;
    }

    GLuint program = glCreateProgram();
    if (program) {
        glAttachShader(program, vertexShader);
        checkGlError("glAttachShader");
        glAttachShader(program, pixelShader);
        checkGlError("glAttachShader");
        glLinkProgram(program);
        GLint linkStatus = GL_FALSE;
        glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
        if (linkStatus != GL_TRUE) {
            GLint bufLength = 0;
            glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
            if (bufLength) {
                char* buf = (char*) malloc(bufLength);
                if (buf) {
                    glGetProgramInfoLog(program, bufLength, NULL, buf);
                    LOGE("Could not link program:\n%s\n", buf);
                    free(buf);
                }
            }
            glDeleteProgram(program);
            program = 0;
        }
    }
    return program;
}

GLuint gProgram;
GLuint gvPositionHandle;

bool setupGraphics(int w, int h) {

    printGLString("Version", GL_VERSION);
    printGLString("Vendor", GL_VENDOR);
    printGLString("Renderer", GL_RENDERER);
    printGLString("Extensions", GL_EXTENSIONS);

    LOGI("setupGraphics(%d, %d)", w, h);
    gProgram = createProgram(gVertexShader.c_str(), gFragmentShader.c_str());
    if (!gProgram) {
        LOGE("Could not create program.");
        return false;
    }
    gvPositionHandle = glGetAttribLocation(gProgram, "vPosition");
    checkGlError("glGetAttribLocation");
    LOGI("glGetAttribLocation(\"vPosition\") = %d\n",
            gvPositionHandle);

    glViewport(0, 0, w, h);
    checkGlError("glViewport");
    return true;
}

const GLfloat gTriangleVertices[] = { 0.0f, 0.5f, -0.5f, -0.5f,
        0.5f, -0.5f };

void renderFrame() {
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    checkGlError("glClearColor");
    glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
    checkGlError("glClear");

    glUseProgram(gProgram);
    checkGlError("glUseProgram");

    glVertexAttribPointer(gvPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, gTriangleVertices);
    checkGlError("glVertexAttribPointer");
    glEnableVertexAttribArray(gvPositionHandle);
    checkGlError("glEnableVertexAttribArray");
    glDrawArrays(GL_TRIANGLES, 0, 3);
    checkGlError("glDrawArrays");
}

extern "C" {
    JNIEXPORT void JNICALL Java_br_odb_nehe_lesson01_GL2JNILib_onCreate(JNIEnv * env, void* reserved, jobject assetManager );
    JNIEXPORT void JNICALL Java_br_odb_nehe_lesson01_GL2JNILib_init(JNIEnv * env, jobject obj,  jint width, jint height);
    JNIEXPORT void JNICALL Java_br_odb_nehe_lesson01_GL2JNILib_step(JNIEnv * env, jobject obj);
};

JNIEXPORT void JNICALL Java_br_odb_nehe_lesson01_GL2JNILib_onCreate(JNIEnv * env, void* reserved, jobject assetManager ) {
    loadShaders( env, assetManager );
}

JNIEXPORT void JNICALL Java_br_odb_nehe_lesson01_GL2JNILib_init(JNIEnv * env, jobject obj,  jint width, jint height)
{
    setupGraphics(width, height);
}

JNIEXPORT void JNICALL Java_br_odb_nehe_lesson01_GL2JNILib_step(JNIEnv * env, jobject obj)
{
    renderFrame();
}
<commit_msg>Remove every thing we don't need.<commit_after>/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// OpenGL ES 2.0 code

#include <jni.h>
#include <android/log.h>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <string>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define  LOG_TAG    "libgl2jni"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

static void printGLString(const char *name, GLenum s) {
    const char *v = (const char *) glGetString(s);
    LOGI("GL %s = %s\n", name, v);
}

static void checkGlError(const char* op) {
    for (GLint error = glGetError(); error; error
            = glGetError()) {
        LOGI("after %s() glError (0x%x)\n", op, error);
    }
}

static std::string gVertexShader;
static std::string gFragmentShader;

static int android_read(void* cookie, char* buf, int size) {
    return AAsset_read((AAsset*)cookie, buf, size);
}

static int android_write(void* cookie, const char* buf, int size) {
    return EACCES; // can't provide write access to the apk
}

static fpos_t android_seek(void* cookie, fpos_t offset, int whence) {
    return AAsset_seek((AAsset*)cookie, offset, whence);
}

static int android_close(void* cookie) {
    AAsset_close((AAsset*)cookie);
    return 0;
}

FILE* android_fopen(const char* fname, const char* mode, AAssetManager *assetManager) {
    if(mode[0] == 'w') return NULL;

    AAsset* asset = AAssetManager_open( assetManager, fname, 0);
    if(!asset) return NULL;

    return funopen(asset, android_read, android_write, android_seek, android_close);
}

std::string readShaderToString( FILE* fileDescriptor ) {
    const unsigned N=1024;
    std::string total;
    while (true) {
        char buffer[ N ];
        size_t read = fread((void *)&buffer[0], 1, N, fileDescriptor);
        if (read) {
            for ( int c = 0; c <  read; ++c ) {
                total.push_back( buffer[ c ] );
            }
        }
        if (read < N) {
            break;
        }
    }

    return total;
}

void loadShaders( JNIEnv* env, jobject& obj ) {
    AAssetManager *asset_manager = AAssetManager_fromJava( env, obj );
    FILE* fd;
    fd = android_fopen( "vertex.glsl", "r", asset_manager );
    gVertexShader = readShaderToString( fd );
    fclose( fd );
    fd = android_fopen( "fragment.glsl", "r", asset_manager );
    gFragmentShader = readShaderToString( fd );
    fclose( fd );
}

GLuint loadShader(GLenum shaderType, const char* pSource) {
    GLuint shader = glCreateShader(shaderType);
    if (shader) {
        glShaderSource(shader, 1, &pSource, NULL);
        glCompileShader(shader);
        GLint compiled = 0;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
        if (!compiled) {
            GLint infoLen = 0;
            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
            if (infoLen) {
                char* buf = (char*) malloc(infoLen);
                if (buf) {
                    glGetShaderInfoLog(shader, infoLen, NULL, buf);
                    LOGE("Could not compile shader %d:\n%s\n",
                            shaderType, buf);
                    free(buf);
                }
                glDeleteShader(shader);
                shader = 0;
            }
        }
    }
    return shader;
}

GLuint createProgram(const char* pVertexSource, const char* pFragmentSource) {
    GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource);
    if (!vertexShader) {
        return 0;
    }

    GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource);
    if (!pixelShader) {
        return 0;
    }

    GLuint program = glCreateProgram();
    if (program) {
        glAttachShader(program, vertexShader);
        checkGlError("glAttachShader");
        glAttachShader(program, pixelShader);
        checkGlError("glAttachShader");
        glLinkProgram(program);
        GLint linkStatus = GL_FALSE;
        glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
        if (linkStatus != GL_TRUE) {
            GLint bufLength = 0;
            glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
            if (bufLength) {
                char* buf = (char*) malloc(bufLength);
                if (buf) {
                    glGetProgramInfoLog(program, bufLength, NULL, buf);
                    LOGE("Could not link program:\n%s\n", buf);
                    free(buf);
                }
            }
            glDeleteProgram(program);
            program = 0;
        }
    }
    return program;
}

GLuint gProgram;

bool setupGraphics(int w, int h) {

    printGLString("Version", GL_VERSION);
    printGLString("Vendor", GL_VENDOR);
    printGLString("Renderer", GL_RENDERER);
    printGLString("Extensions", GL_EXTENSIONS);

    LOGI("setupGraphics(%d, %d)", w, h);
    gProgram = createProgram(gVertexShader.c_str(), gFragmentShader.c_str());
    if (!gProgram) {
        LOGE("Could not create program.");
        return false;
    }
    glViewport(0, 0, w, h);
    checkGlError("glViewport");
    return true;
}

void renderFrame() {
    glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
    checkGlError("glClearColor");
    glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
    checkGlError("glClear");

    glUseProgram(gProgram);
    checkGlError("glUseProgram");
}

extern "C" {
    JNIEXPORT void JNICALL Java_br_odb_nehe_lesson01_GL2JNILib_onCreate(JNIEnv * env, void* reserved, jobject assetManager );
    JNIEXPORT void JNICALL Java_br_odb_nehe_lesson01_GL2JNILib_init(JNIEnv * env, jobject obj,  jint width, jint height);
    JNIEXPORT void JNICALL Java_br_odb_nehe_lesson01_GL2JNILib_step(JNIEnv * env, jobject obj);
};

JNIEXPORT void JNICALL Java_br_odb_nehe_lesson01_GL2JNILib_onCreate(JNIEnv * env, void* reserved, jobject assetManager ) {
    loadShaders( env, assetManager );
}

JNIEXPORT void JNICALL Java_br_odb_nehe_lesson01_GL2JNILib_init(JNIEnv * env, jobject obj,  jint width, jint height)
{
    setupGraphics(width, height);
}

JNIEXPORT void JNICALL Java_br_odb_nehe_lesson01_GL2JNILib_step(JNIEnv * env, jobject obj)
{
    renderFrame();
}
<|endoftext|>


<commit_before>// Benchmark concurrent stats and links/unlinks.  Ideally, this will
// move a single cache line between stat and link: the cache line for
// the link count.  Our hypothesis is that this is sufficient to limit
// scalability, while tweaking stat to not return the link count will
// lead to perfect scalability of stat.

#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/utsname.h>

#include "amd64.h"
#include "histogram.hh"
#include "xsys.h"

#if defined(XV6_USER)
#include "pthread.h"
#else
#include <pthread.h>
#endif

#if defined(LINUX)
//#define RECORD_PMC 3
#elif defined(HW_tom)
#define RECORD_PMC 0
#endif

#if MTRACE
#include "mtrace.h"
#endif

enum { warmup_secs = 1 };
enum { duration = 5 };

static bool omit_nlink;
static pthread_barrier_t bar, bar2;
static int filefd;
static uint64_t start_tsc[256], stop_tsc[256];
static uint64_t tsc_stat[256], tsc_link[256], pmc_stat[256];
static uint64_t count[256];
static volatile bool stop __mpalign__;
static volatile bool warmup;
static __padout__ __attribute__((unused));

static histogram_log2<uint64_t, 1<<20> tsc_hist[256];

void
mystat()
{
  struct stat st;
#if defined(XV6_USER)
  fstatx(filefd, &st, omit_nlink ? STAT_OMIT_NLINK : STAT_NO_FLAGS);
#else
  fstat(filefd, &st);
#endif
}

void*
timer_thread(void *)
{
  warmup = true;
  pthread_barrier_wait(&bar);
  pthread_barrier_wait(&bar2);
  sleep(warmup_secs);
  warmup = false;
  sleep(duration);
  stop = true;
  return NULL;
}

void*
do_stat(void *opaque)
{
  uintptr_t cpu = (uintptr_t)opaque;
  setaffinity(cpu);

  pthread_barrier_wait(&bar);
  pthread_barrier_wait(&bar2);

  bool mywarmup = true;
  uint64_t mycount = 0;
  uint64_t pmc1 = 0, pmc2 = 0;
  while (!stop) {
    if (__builtin_expect(warmup != mywarmup, 0)) {
      mywarmup = warmup;
      mycount = 0;
      start_tsc[cpu] = rdtsc();
#ifdef RECORD_PMC
      pmc1 = rdpmc(RECORD_PMC);
#endif
    }
    mystat();
    ++mycount;
  }
#ifdef RECORD_PMC
  pmc2 = rdpmc(RECORD_PMC);
#endif

  stop_tsc[cpu] = rdtsc();
  count[cpu] = mycount;
  tsc_stat[cpu] = stop_tsc[cpu] - start_tsc[cpu];
  pmc_stat[cpu] = pmc2 - pmc1;
  return NULL;
}

void*
do_link(void *opaque)
{
  uintptr_t cpu = (uintptr_t)opaque;
  setaffinity(cpu);

  char path[32];
  snprintf(path, sizeof(path), "%d", (int)cpu);
  mkdir(path, 0777);
  snprintf(path, sizeof(path), "%d/link", (int)cpu);

  pthread_barrier_wait(&bar);
  pthread_barrier_wait(&bar2);

  bool mywarmup = true;
  uint64_t mycount = 0;
  while (!stop) {
    if (__builtin_expect(warmup != mywarmup, 0)) {
      mywarmup = warmup;
      mycount = 0;
      start_tsc[cpu] = rdtsc();
    }
    link("0/file", path);
    unlink(path);
    ++mycount;
  }

  stop_tsc[cpu] = rdtsc();
  count[cpu] = mycount;
  tsc_link[cpu] = stop_tsc[cpu] - start_tsc[cpu];
  return NULL;
}

#ifdef LINUX
static inline uint64_t
rdpmc(uint32_t ecx)
{
  uint32_t hi, lo;
  __asm volatile("rdpmc" : "=a" (lo), "=d" (hi) : "c" (ecx));
  return ((uint64_t) lo) | (((uint64_t) hi) << 32);
}
#endif

void*
do_both(void *opaque)
{
  uintptr_t cpu = (uintptr_t)opaque;
  setaffinity(cpu);

  char path[32];
  snprintf(path, sizeof(path), "%d", (int)cpu);
  mkdir(path, 0777);
  snprintf(path, sizeof(path), "%d/link", (int)cpu);

  pthread_barrier_wait(&bar);
  pthread_barrier_wait(&bar2);

  bool mywarmup = true;
  uint64_t mycount = 0, ltsc_stat = 0, ltsc_link = 0, lpmc_stat = 0;
  while (!stop) {
    if (__builtin_expect(warmup != mywarmup, 0)) {
      mywarmup = warmup;
      mycount = ltsc_stat = ltsc_link = lpmc_stat = 0;
      start_tsc[cpu] = rdtsc();
    }
#ifdef RECORD_PMC
    uint64_t pmc1 = rdpmc(RECORD_PMC);
#endif
    uint64_t tsc1 = rdtsc();
    mystat();
    uint64_t tsc2 = rdtsc();
#ifdef RECORD_PMC
    uint64_t pmc2 = rdpmc(RECORD_PMC);
    if (pmc2 - pmc1 < 5000)
      lpmc_stat += pmc2 - pmc1;
#endif
    ltsc_stat += tsc2 - tsc1;
    tsc_hist[cpu] += tsc2 - tsc1;
    link("0/file", path);
    unlink(path);
    uint64_t tsc3 = rdtsc();
    ltsc_link += tsc3 - tsc2;
    ++mycount;
  }

  stop_tsc[cpu] = rdtsc();
  count[cpu] = mycount;
  tsc_stat[cpu] = ltsc_stat;
  tsc_link[cpu] = ltsc_link;
  pmc_stat[cpu] = lpmc_stat;
  return NULL;
}

uint64_t
summarize_tsc(const char *label, uint64_t tscs[], unsigned count)
{
  uint64_t min = tscs[0], max = tscs[0], total = 0;
  for (unsigned i = 0; i < count; ++i) {
    if (tscs[i] < min)
      min = tscs[i];
    if (tscs[i] > max)
      max = tscs[i];
    total += tscs[i];
  }
  printf("%lu cycles %s skew\n", max - min, label);
  return total/count;
}

template<class T>
T
sum(T v[], unsigned count)
{
  T res{};
  for (unsigned i = 0; i < count; ++i)
    res += v[i];
  return res;
}

int
main(int argc, char **argv)
{
  if (argc < 3)
    die("usage: %s {no-}nlink nthreads|{nstatthreads nlinkthreads}", argv[0]);

  omit_nlink = strcmp(argv[1], "no-nlink") == 0;
  if (!omit_nlink && strcmp(argv[1], "nlink") != 0)
    die("bad nlink argument");
#if !defined(XV6_USER)
  if (omit_nlink)
    die("no-nlink not supported on Linux");
#endif
  int both = argc == 3;
  int nstats = atoi(argv[2]);
  int nlinks = both ? 0 : atoi(argv[3]);

  struct utsname uts;
  uname(&uts);

  printf("# --cores=%d --duration=%ds --nlink=%s", nstats+nlinks, duration,
         omit_nlink ? "false" : "true");
  if (both)
    printf("\n");
  else
    printf(" --stats=%d --links=%d\n", nstats, nlinks);

#if !defined(LINUX) && defined(RECORD_PMC)
  // Configure PMC
  // L2 cache misses
  perf_start(PERF_SEL_USR|PERF_SEL_OS|PERF_SEL_ENABLE|(0x2|0x8)<<8|0x7e, 0);
  printf("# --pmc=\"L2 cache misses\"\n");
  // L1 cache misses
//  perf_start(PERF_SEL_USR|PERF_SEL_OS|PERF_SEL_ENABLE|(1<<24)|(0x1f<<8)|0x42, 0);
  // Retired instructions
//  perf_start(PERF_SEL_USR|PERF_SEL_OS|PERF_SEL_ENABLE|0xc0, 0);
  // Retired mispredicted branch instructions
//  perf_start(PERF_SEL_USR|PERF_SEL_OS|PERF_SEL_ENABLE|0xc3, 0);
  // Dispatch stalls
//  perf_start(PERF_SEL_USR|PERF_SEL_OS|PERF_SEL_ENABLE|0xd1, 0);
  // Dispatch stall on LS full
//  perf_start(PERF_SEL_USR|PERF_SEL_OS|PERF_SEL_ENABLE|0xd8, 0);
  // LS buffer 2 full
//  perf_start(PERF_SEL_USR|PERF_SEL_OS|PERF_SEL_ENABLE|0x23, 0);
#endif

  // Set up file system
  mkdir("linkbench-d", 0777);
  chdir("linkbench-d");
  mkdir("0", 0777);
  filefd = openat(AT_FDCWD, "0/file", O_CREAT|O_RDWR, 0666);
  if (filefd < 0)
    die("openat failed");

  pthread_barrier_init(&bar, 0, nstats + nlinks + 2);
  pthread_barrier_init(&bar2, 0, nstats + nlinks + 2);

  // Run benchmark
  pthread_t timer;
  pthread_create(&timer, NULL, timer_thread, NULL);

  pthread_t *threads = (pthread_t*)malloc(sizeof(*threads) * (nstats + nlinks));
  for (uintptr_t i = 0; i < nstats + nlinks; ++i)
    pthread_create(&threads[i], NULL,
                   both ? do_both :
                   i < nstats ? do_stat :
                   do_link, (void*)i);

  pthread_barrier_wait(&bar);

#if MTRACE
  mtenable_type(mtrace_record_ascope, "xv6-linkbench");
#endif

  pthread_barrier_wait(&bar2);

  // Wait
  xpthread_join(timer);
  for (int i = 0; i < nstats + nlinks; ++i)
    xpthread_join(threads[i]);

#if MTRACE
  mtdisable("xv6-linkbench");
#endif

  // Summarize
  uint64_t start_avg = summarize_tsc("start", start_tsc, nstats + nlinks);
  uint64_t stop_avg = summarize_tsc("stop", stop_tsc, nstats + nlinks);
  printf("%lu cycles\n", stop_avg - start_avg);
  uint64_t stats, links;
  if (both)
    stats = links = sum(count, nstats + nlinks);
  else
    stats = sum(count, nstats), links = sum(count + nstats, nlinks);
  printf("%lu stats\n", stats);
  if (stats) {
    printf("%lu cycles/stat\n", sum(tsc_stat, nstats + nlinks) / stats);
#ifdef RECORD_PMC
    printf("%lu pmc\n", sum(pmc_stat, nstats + nlinks));
    printf("%lu pmc/stat\n", sum(pmc_stat, nstats + nlinks) / stats);
#ifdef LINUX
    // xv6 doesn't have floating point support
    printf("%g pmc/stat\n", sum(pmc_stat, nstats + nlinks) / (double)stats);
#endif
#endif
  }
  printf("%lu links\n", links);
  if (links)
    printf("%lu cycles/link\n", sum(tsc_link, nstats + nlinks) / links);

  // printf("stat tsc histogram: ");
  // auto hist = sum(tsc_hist, nstats + nlinks);
  // hist.print_stats();
  // hist.print_bars();
  // printf("\n");
  // hist.print();

  printf("\n");
}
<commit_msg>linkbench: Real time statistics<commit_after>// Benchmark concurrent stats and links/unlinks.  Ideally, this will
// move a single cache line between stat and link: the cache line for
// the link count.  Our hypothesis is that this is sufficient to limit
// scalability, while tweaking stat to not return the link count will
// lead to perfect scalability of stat.

#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/utsname.h>

#include "amd64.h"
#include "histogram.hh"
#include "libutil.h"
#include "xsys.h"

#if defined(XV6_USER)
#include "pthread.h"
#else
#include <pthread.h>
#endif

#if defined(LINUX)
//#define RECORD_PMC 3
#elif defined(HW_tom)
#define RECORD_PMC 0
#endif

#if MTRACE
#include "mtrace.h"
#endif

enum { warmup_secs = 1 };
enum { duration = 5 };

static bool omit_nlink;
static pthread_barrier_t bar, bar2;
static int filefd;
static uint64_t start_tsc[256], stop_tsc[256];
static uint64_t start_usec[256], stop_usec[256];
static uint64_t tsc_stat[256], tsc_link[256], pmc_stat[256];
static uint64_t count[256];
static volatile bool stop __mpalign__;
static volatile bool warmup;
static __padout__ __attribute__((unused));

static histogram_log2<uint64_t, 1<<20> tsc_hist[256];

void
mystat()
{
  struct stat st;
#if defined(XV6_USER)
  fstatx(filefd, &st, omit_nlink ? STAT_OMIT_NLINK : STAT_NO_FLAGS);
#else
  fstat(filefd, &st);
#endif
}

void*
timer_thread(void *)
{
  warmup = true;
  pthread_barrier_wait(&bar);
  pthread_barrier_wait(&bar2);
  sleep(warmup_secs);
  warmup = false;
  sleep(duration);
  stop = true;
  return NULL;
}

void*
do_stat(void *opaque)
{
  uintptr_t cpu = (uintptr_t)opaque;
  setaffinity(cpu);

  pthread_barrier_wait(&bar);
  pthread_barrier_wait(&bar2);

  bool mywarmup = true;
  uint64_t mycount = 0;
  uint64_t pmc1 = 0, pmc2 = 0;
  while (!stop) {
    if (__builtin_expect(warmup != mywarmup, 0)) {
      mywarmup = warmup;
      mycount = 0;
      start_usec[cpu] = now_usec();
      start_tsc[cpu] = rdtsc();
#ifdef RECORD_PMC
      pmc1 = rdpmc(RECORD_PMC);
#endif
    }
    mystat();
    ++mycount;
  }
#ifdef RECORD_PMC
  pmc2 = rdpmc(RECORD_PMC);
#endif

  stop_usec[cpu] = now_usec();
  stop_tsc[cpu] = rdtsc();
  count[cpu] = mycount;
  tsc_stat[cpu] = stop_tsc[cpu] - start_tsc[cpu];
  pmc_stat[cpu] = pmc2 - pmc1;
  return NULL;
}

void*
do_link(void *opaque)
{
  uintptr_t cpu = (uintptr_t)opaque;
  setaffinity(cpu);

  char path[32];
  snprintf(path, sizeof(path), "%d", (int)cpu);
  mkdir(path, 0777);
  snprintf(path, sizeof(path), "%d/link", (int)cpu);

  pthread_barrier_wait(&bar);
  pthread_barrier_wait(&bar2);

  bool mywarmup = true;
  uint64_t mycount = 0;
  while (!stop) {
    if (__builtin_expect(warmup != mywarmup, 0)) {
      mywarmup = warmup;
      mycount = 0;
      start_usec[cpu] = now_usec();
      start_tsc[cpu] = rdtsc();
    }
    link("0/file", path);
    unlink(path);
    ++mycount;
  }

  stop_usec[cpu] = now_usec();
  stop_tsc[cpu] = rdtsc();
  count[cpu] = mycount;
  tsc_link[cpu] = stop_tsc[cpu] - start_tsc[cpu];
  return NULL;
}

#ifdef LINUX
static inline uint64_t
rdpmc(uint32_t ecx)
{
  uint32_t hi, lo;
  __asm volatile("rdpmc" : "=a" (lo), "=d" (hi) : "c" (ecx));
  return ((uint64_t) lo) | (((uint64_t) hi) << 32);
}
#endif

void*
do_both(void *opaque)
{
  uintptr_t cpu = (uintptr_t)opaque;
  setaffinity(cpu);

  char path[32];
  snprintf(path, sizeof(path), "%d", (int)cpu);
  mkdir(path, 0777);
  snprintf(path, sizeof(path), "%d/link", (int)cpu);

  pthread_barrier_wait(&bar);
  pthread_barrier_wait(&bar2);

  bool mywarmup = true;
  uint64_t mycount = 0, ltsc_stat = 0, ltsc_link = 0, lpmc_stat = 0;
  while (!stop) {
    if (__builtin_expect(warmup != mywarmup, 0)) {
      mywarmup = warmup;
      mycount = ltsc_stat = ltsc_link = lpmc_stat = 0;
      start_usec[cpu] = now_usec();
      start_tsc[cpu] = rdtsc();
    }
#ifdef RECORD_PMC
    uint64_t pmc1 = rdpmc(RECORD_PMC);
#endif
    uint64_t tsc1 = rdtsc();
    mystat();
    uint64_t tsc2 = rdtsc();
#ifdef RECORD_PMC
    uint64_t pmc2 = rdpmc(RECORD_PMC);
    if (pmc2 - pmc1 < 5000)
      lpmc_stat += pmc2 - pmc1;
#endif
    ltsc_stat += tsc2 - tsc1;
    tsc_hist[cpu] += tsc2 - tsc1;
    link("0/file", path);
    unlink(path);
    uint64_t tsc3 = rdtsc();
    ltsc_link += tsc3 - tsc2;
    ++mycount;
  }

  stop_usec[cpu] = now_usec();
  stop_tsc[cpu] = rdtsc();
  count[cpu] = mycount;
  tsc_stat[cpu] = ltsc_stat;
  tsc_link[cpu] = ltsc_link;
  pmc_stat[cpu] = lpmc_stat;
  return NULL;
}

uint64_t
summarize_ts(const char *label, uint64_t ts[], unsigned count)
{
  uint64_t min = ts[0], max = ts[0], total = 0;
  for (unsigned i = 0; i < count; ++i) {
    if (ts[i] < min)
      min = ts[i];
    if (ts[i] > max)
      max = ts[i];
    total += ts[i];
  }
  printf("%lu %s skew\n", max - min, label);
  return total/count;
}

template<class T>
T
sum(T v[], unsigned count)
{
  T res{};
  for (unsigned i = 0; i < count; ++i)
    res += v[i];
  return res;
}

int
main(int argc, char **argv)
{
  if (argc < 3)
    die("usage: %s {no-}nlink nthreads|{nstatthreads nlinkthreads}", argv[0]);

  omit_nlink = strcmp(argv[1], "no-nlink") == 0;
  if (!omit_nlink && strcmp(argv[1], "nlink") != 0)
    die("bad nlink argument");
#if !defined(XV6_USER)
  if (omit_nlink)
    die("no-nlink not supported on Linux");
#endif
  int both = argc == 3;
  int nstats = atoi(argv[2]);
  int nlinks = both ? 0 : atoi(argv[3]);

  struct utsname uts;
  uname(&uts);

  printf("# --cores=%d --duration=%ds --nlink=%s", nstats+nlinks, duration,
         omit_nlink ? "false" : "true");
  if (both)
    printf("\n");
  else
    printf(" --stats=%d --links=%d\n", nstats, nlinks);

#if !defined(LINUX) && defined(RECORD_PMC)
  // Configure PMC
  // L2 cache misses
  perf_start(PERF_SEL_USR|PERF_SEL_OS|PERF_SEL_ENABLE|(0x2|0x8)<<8|0x7e, 0);
  printf("# --pmc=\"L2 cache misses\"\n");
  // L1 cache misses
//  perf_start(PERF_SEL_USR|PERF_SEL_OS|PERF_SEL_ENABLE|(1<<24)|(0x1f<<8)|0x42, 0);
  // Retired instructions
//  perf_start(PERF_SEL_USR|PERF_SEL_OS|PERF_SEL_ENABLE|0xc0, 0);
  // Retired mispredicted branch instructions
//  perf_start(PERF_SEL_USR|PERF_SEL_OS|PERF_SEL_ENABLE|0xc3, 0);
  // Dispatch stalls
//  perf_start(PERF_SEL_USR|PERF_SEL_OS|PERF_SEL_ENABLE|0xd1, 0);
  // Dispatch stall on LS full
//  perf_start(PERF_SEL_USR|PERF_SEL_OS|PERF_SEL_ENABLE|0xd8, 0);
  // LS buffer 2 full
//  perf_start(PERF_SEL_USR|PERF_SEL_OS|PERF_SEL_ENABLE|0x23, 0);
#endif

  // Set up file system
  mkdir("linkbench-d", 0777);
  chdir("linkbench-d");
  mkdir("0", 0777);
  filefd = openat(AT_FDCWD, "0/file", O_CREAT|O_RDWR, 0666);
  if (filefd < 0)
    die("openat failed");

  pthread_barrier_init(&bar, 0, nstats + nlinks + 2);
  pthread_barrier_init(&bar2, 0, nstats + nlinks + 2);

  // Run benchmark
  pthread_t timer;
  pthread_create(&timer, NULL, timer_thread, NULL);

  pthread_t *threads = (pthread_t*)malloc(sizeof(*threads) * (nstats + nlinks));
  for (uintptr_t i = 0; i < nstats + nlinks; ++i)
    pthread_create(&threads[i], NULL,
                   both ? do_both :
                   i < nstats ? do_stat :
                   do_link, (void*)i);

  pthread_barrier_wait(&bar);

#if MTRACE
  mtenable_type(mtrace_record_ascope, "xv6-linkbench");
#endif

  pthread_barrier_wait(&bar2);

  // Wait
  xpthread_join(timer);
  for (int i = 0; i < nstats + nlinks; ++i)
    xpthread_join(threads[i]);

#if MTRACE
  mtdisable("xv6-linkbench");
#endif

  // Summarize
  uint64_t start_tsc_avg = summarize_ts("start cycles", start_tsc, nstats + nlinks);
  uint64_t stop_tsc_avg = summarize_ts("stop cycles", stop_tsc, nstats + nlinks);
  printf("%lu cycles\n", stop_tsc_avg - start_tsc_avg);

  uint64_t start_usec_avg = summarize_ts("start usec", start_usec, nstats + nlinks);
  uint64_t stop_usec_avg = summarize_ts("stop usec", stop_usec, nstats + nlinks);
  uint64_t usec = stop_usec_avg - start_usec_avg;
  printf("%f secs\n", (double)usec / 1e6);

  uint64_t stats, links;
  if (both)
    stats = links = sum(count, nstats + nlinks);
  else
    stats = sum(count, nstats), links = sum(count + nstats, nlinks);
  printf("%lu stats\n", stats);
  if (stats) {
    printf("%lu cycles/stat\n", sum(tsc_stat, nstats + nlinks) / stats);
    printf("%lu stats/sec\n", stats * 1000000 / usec);
#ifdef RECORD_PMC
    printf("%lu pmc\n", sum(pmc_stat, nstats + nlinks));
    printf("%lu pmc/stat\n", sum(pmc_stat, nstats + nlinks) / stats);
#ifdef LINUX
    // xv6 doesn't have floating point support
    printf("%g pmc/stat\n", sum(pmc_stat, nstats + nlinks) / (double)stats);
#endif
#endif
  }
  printf("%lu links\n", links);
  if (links) {
    printf("%lu cycles/link\n", sum(tsc_link, nstats + nlinks) / links);
    printf("%lu links/sec\n", links * 1000000 / usec);
  }

  // printf("stat tsc histogram: ");
  // auto hist = sum(tsc_hist, nstats + nlinks);
  // hist.print_stats();
  // hist.print_bars();
  // printf("\n");
  // hist.print();

  printf("\n");
}
<|endoftext|>


<commit_before>//===--- DebuggerTestingTransform.cpp - Transform for debugger testing ----===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
///
/// \file This transform inserts instrumentation which the debugger can use to
/// test its expression evaluation facilities.
///
//===----------------------------------------------------------------------===//

#include "CodeSynthesis.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/ASTNode.h"
#include "swift/AST/ASTWalker.h"
#include "swift/AST/Decl.h"
#include "swift/AST/DeclContext.h"
#include "swift/AST/Expr.h"
#include "swift/AST/Module.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/SourceFile.h"
#include "swift/AST/Stmt.h"
#include "swift/Subsystems.h"

#include "TypeChecker.h"

using namespace swift;

namespace {

/// Find available closure discriminators.
///
/// The parser typically takes care of assigning unique discriminators to
/// closures, but the parser is unavailable during semantic analysis.
class DiscriminatorFinder : public ASTWalker {
  unsigned NextDiscriminator = 0;

public:
  Expr *walkToExprPost(Expr *E) override {
    auto *ACE = dyn_cast<AbstractClosureExpr>(E);
    if (!ACE)
      return E;

    unsigned Discriminator = ACE->getDiscriminator();
    assert(Discriminator != AbstractClosureExpr::InvalidDiscriminator &&
           "Existing closures should have valid discriminators");
    if (Discriminator >= NextDiscriminator)
      NextDiscriminator = Discriminator + 1;
    return E;
  }

  // Get the next available closure discriminator.
  unsigned getNextDiscriminator() {
    if (NextDiscriminator == AbstractClosureExpr::InvalidDiscriminator)
      llvm::report_fatal_error("Out of valid closure discriminators");
    return NextDiscriminator++;
  }
};

/// Instrument decls with sanity-checks which the debugger can evaluate.
class DebuggerTestingTransform : public ASTWalker {
  ASTContext &Ctx;
  DiscriminatorFinder &DF;
  std::vector<DeclContext *> LocalDeclContextStack;
  const DeclNameRef StringForPrintObjectName;
  const DeclNameRef DebuggerTestingCheckExpectName;

public:
  DebuggerTestingTransform(ASTContext &Ctx, DiscriminatorFinder &DF)
      : Ctx(Ctx), DF(DF),
        StringForPrintObjectName(Ctx.getIdentifier("_stringForPrintObject")),
        DebuggerTestingCheckExpectName(
            Ctx.getIdentifier("_debuggerTestingCheckExpect")) {}

  bool walkToDeclPre(Decl *D) override {
    pushLocalDeclContext(D);

    // Skip implicit decls, because the debugger isn't used to step through
    // these.
    if (D->isImplicit())
      return false;

    // Whitelist the kinds of decls to transform.
    // TODO: Expand the set of decls visited here.
    if (auto *FD = dyn_cast<AbstractFunctionDecl>(D))
      return FD->getBody();
    if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D))
      return TLCD->getBody();
    if (isa<NominalTypeDecl>(D))
      return true;
    return false;
  }

  bool walkToDeclPost(Decl *D) override {
    popLocalDeclContext(D);
    return true;
  }

  std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
    pushLocalDeclContext(E);

    // Whitelist the kinds of exprs to transform.
    // TODO: Expand the set of exprs visited here.
    if (auto *AE = dyn_cast<AssignExpr>(E))
      return insertCheckExpect(AE, AE->getDest());

    return {true, E};
  }

  Expr *walkToExprPost(Expr *E) override {
    popLocalDeclContext(E);
    return E;
  }

private:
  /// Return N as a local DeclContext if possible, or return nullptr.
  DeclContext *getLocalDeclContext(ASTNode N) const {
    DeclContext *DC = N.getAsDeclContext();
    return (DC && DC->isLocalContext()) ? DC : nullptr;
  }

  /// If N is a local DeclContext, push it onto the context stack.
  void pushLocalDeclContext(ASTNode N) {
    if (auto *LDC = getLocalDeclContext(N))
      LocalDeclContextStack.push_back(LDC);
  }

  /// If N is a local DeclContext, pop it off the context stack.
  void popLocalDeclContext(ASTNode N) {
    if (getLocalDeclContext(N))
      LocalDeclContextStack.pop_back();
  }

  /// Get the current local DeclContext. This is used to create closures in the
  /// right context.
  DeclContext *getCurrentDeclContext() const {
    assert(!LocalDeclContextStack.empty() && "Missing decl context");
    return LocalDeclContextStack.back();
  }

  /// Try to extract a DeclRefExpr or MemberRefExpr from the expression.
  Expr *extractDeclOrMemberRef(Expr *E) {
    while (!isa<DeclRefExpr>(E) && !isa<MemberRefExpr>(E)) {
      // TODO: Try more ways to extract interesting decl refs.
      if (auto *Subscript = dyn_cast<SubscriptExpr>(E))
        E = Subscript->getBase();
      else if (auto *InOut = dyn_cast<InOutExpr>(E))
        E = InOut->getSubExpr();
      else if (auto *MemberRef = dyn_cast<MemberRefExpr>(E))
        E = MemberRef->getBase();
      else
        return nullptr;
    }
    return E;
  }

  /// Attempt to create a functionally-equivalent replacement for OriginalExpr,
  /// given that DstExpr identifies the target of some mutating update, and that
  /// DstExpr is a subexpression of OriginalExpr.
  ///
  /// The return value contains 1) a flag indicating whether or not to
  /// recursively transform the children of the transformed expression, and 2)
  /// the transformed expression itself.
  std::pair<bool, Expr *> insertCheckExpect(Expr *OriginalExpr, Expr *DstExpr) {
    auto *DstRef = extractDeclOrMemberRef(DstExpr);
    if (!DstRef)
      return {true, OriginalExpr};

    ValueDecl *DstDecl;
    if (auto *DRE = dyn_cast<DeclRefExpr>(DstRef))
      DstDecl = DRE->getDecl();
    else {
      auto *MRE = cast<MemberRefExpr>(DstRef);
      DstDecl = MRE->getMember().getDecl();
    }
    if (!DstDecl->hasName())
      return {true, OriginalExpr};

    // Don't capture variables which aren't default-initialized.
    if (auto *VD = dyn_cast<VarDecl>(DstDecl))
      if (!VD->isParentInitialized() &&
          !(isa<ParamDecl>(VD) &&
            cast<ParamDecl>(VD)->isInOut()))
        return {true, OriginalExpr};

    // Rewrite the original expression into this:
    // call
    //   closure {
    //     $OriginalExpr
    //     checkExpect("$Varname", _stringForPrintObject($Varname))
    //   }

    // Create "$Varname".
    llvm::SmallString<256> DstNameBuf;
    const DeclName DstDN = DstDecl->getName();
    StringRef DstName = Ctx.AllocateCopy(DstDN.getString(DstNameBuf));
    assert(!DstName.empty() && "Varname must be non-empty");
    Expr *Varname = new (Ctx) StringLiteralExpr(DstName, SourceRange());
    Varname->setImplicit(true);

    // Create _stringForPrintObject($Varname).
    auto *PODeclRef = new (Ctx)
        UnresolvedDeclRefExpr(StringForPrintObjectName,
                              DeclRefKind::Ordinary, DeclNameLoc());
    auto *POArgList = ArgumentList::forImplicitUnlabeled(Ctx, {DstRef});
    auto *POCall = CallExpr::createImplicit(Ctx, PODeclRef, POArgList);
    POCall->setThrows(false);

    // Create the call to checkExpect.
    UnresolvedDeclRefExpr *CheckExpectDRE = new (Ctx)
        UnresolvedDeclRefExpr(DebuggerTestingCheckExpectName,
                              DeclRefKind::Ordinary, DeclNameLoc());
    auto *CheckArgList =
        ArgumentList::forImplicitUnlabeled(Ctx, {Varname, POCall});
    auto *CheckExpectExpr =
        CallExpr::createImplicit(Ctx, CheckExpectDRE, CheckArgList);
    CheckExpectExpr->setThrows(false);

    // Create the closure.
    auto *Params = ParameterList::createEmpty(Ctx);
    auto *Closure = new (Ctx)
        ClosureExpr(DeclAttributes(), SourceRange(), nullptr, Params,
                    SourceLoc(), SourceLoc(),
                    SourceLoc(), SourceLoc(), nullptr,
                    DF.getNextDiscriminator(), getCurrentDeclContext());
    Closure->setImplicit(true);

    // TODO: Save and return the value of $OriginalExpr.
    ASTNode ClosureElements[] = {OriginalExpr, CheckExpectExpr};
    auto *ClosureBody = BraceStmt::create(Ctx, SourceLoc(), ClosureElements,
                                          SourceLoc(), /*Implicit=*/true);
    Closure->setBody(ClosureBody, /*isSingleExpression=*/false);

    // Call the closure.
    auto *ClosureCall = CallExpr::createImplicitEmpty(Ctx, Closure);
    ClosureCall->setThrows(false);

    // TODO: typeCheckExpression() seems to assign types to everything here,
    // but may not be sufficient in some cases.
    Expr *FinalExpr = ClosureCall;
    if (!TypeChecker::typeCheckExpression(FinalExpr, getCurrentDeclContext(),
                                          /*contextualInfo=*/{}))
      llvm::report_fatal_error("Could not type-check instrumentation");

    // Captures have to be computed after the closure is type-checked. This
    // ensures that the type checker can infer <noescape> for captured values.
    TypeChecker::computeCaptures(Closure);

    return {false, FinalExpr};
  }
};

} // end anonymous namespace

void swift::performDebuggerTestingTransform(SourceFile &SF) {
  // Walk over all decls in the file to find the next available closure
  // discriminator.
  DiscriminatorFinder DF;
  for (Decl *D : SF.getTopLevelDecls())
    D->walk(DF);

  // Instrument the decls with checkExpect() sanity-checks.
  for (Decl *D : SF.getTopLevelDecls()) {
    DebuggerTestingTransform Transform{D->getASTContext(), DF};
    D->walk(Transform);
    swift::verify(D);
  }
}
<commit_msg>[Sema] DebuggerTestingTransform: avoid re-using AST nodes in generated code<commit_after>//===--- DebuggerTestingTransform.cpp - Transform for debugger testing ----===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
///
/// \file This transform inserts instrumentation which the debugger can use to
/// test its expression evaluation facilities.
///
//===----------------------------------------------------------------------===//

#include "CodeSynthesis.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/ASTNode.h"
#include "swift/AST/ASTWalker.h"
#include "swift/AST/Decl.h"
#include "swift/AST/DeclContext.h"
#include "swift/AST/Expr.h"
#include "swift/AST/Module.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/SourceFile.h"
#include "swift/AST/Stmt.h"
#include "swift/Subsystems.h"

#include "TypeChecker.h"

using namespace swift;

namespace {

/// Find available closure discriminators.
///
/// The parser typically takes care of assigning unique discriminators to
/// closures, but the parser is unavailable during semantic analysis.
class DiscriminatorFinder : public ASTWalker {
  unsigned NextDiscriminator = 0;

public:
  Expr *walkToExprPost(Expr *E) override {
    auto *ACE = dyn_cast<AbstractClosureExpr>(E);
    if (!ACE)
      return E;

    unsigned Discriminator = ACE->getDiscriminator();
    assert(Discriminator != AbstractClosureExpr::InvalidDiscriminator &&
           "Existing closures should have valid discriminators");
    if (Discriminator >= NextDiscriminator)
      NextDiscriminator = Discriminator + 1;
    return E;
  }

  // Get the next available closure discriminator.
  unsigned getNextDiscriminator() {
    if (NextDiscriminator == AbstractClosureExpr::InvalidDiscriminator)
      llvm::report_fatal_error("Out of valid closure discriminators");
    return NextDiscriminator++;
  }
};

/// Instrument decls with sanity-checks which the debugger can evaluate.
class DebuggerTestingTransform : public ASTWalker {
  ASTContext &Ctx;
  DiscriminatorFinder &DF;
  std::vector<DeclContext *> LocalDeclContextStack;
  const DeclNameRef StringForPrintObjectName;
  const DeclNameRef DebuggerTestingCheckExpectName;

public:
  DebuggerTestingTransform(ASTContext &Ctx, DiscriminatorFinder &DF)
      : Ctx(Ctx), DF(DF),
        StringForPrintObjectName(Ctx.getIdentifier("_stringForPrintObject")),
        DebuggerTestingCheckExpectName(
            Ctx.getIdentifier("_debuggerTestingCheckExpect")) {}

  bool walkToDeclPre(Decl *D) override {
    pushLocalDeclContext(D);

    // Skip implicit decls, because the debugger isn't used to step through
    // these.
    if (D->isImplicit())
      return false;

    // Whitelist the kinds of decls to transform.
    // TODO: Expand the set of decls visited here.
    if (auto *FD = dyn_cast<AbstractFunctionDecl>(D))
      return FD->getBody();
    if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D))
      return TLCD->getBody();
    if (isa<NominalTypeDecl>(D))
      return true;
    return false;
  }

  bool walkToDeclPost(Decl *D) override {
    popLocalDeclContext(D);
    return true;
  }

  std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
    pushLocalDeclContext(E);

    // Whitelist the kinds of exprs to transform.
    // TODO: Expand the set of exprs visited here.
    if (auto *AE = dyn_cast<AssignExpr>(E))
      return insertCheckExpect(AE, AE->getDest());

    return {true, E};
  }

  Expr *walkToExprPost(Expr *E) override {
    popLocalDeclContext(E);
    return E;
  }

private:
  /// Return N as a local DeclContext if possible, or return nullptr.
  DeclContext *getLocalDeclContext(ASTNode N) const {
    DeclContext *DC = N.getAsDeclContext();
    return (DC && DC->isLocalContext()) ? DC : nullptr;
  }

  /// If N is a local DeclContext, push it onto the context stack.
  void pushLocalDeclContext(ASTNode N) {
    if (auto *LDC = getLocalDeclContext(N))
      LocalDeclContextStack.push_back(LDC);
  }

  /// If N is a local DeclContext, pop it off the context stack.
  void popLocalDeclContext(ASTNode N) {
    if (getLocalDeclContext(N))
      LocalDeclContextStack.pop_back();
  }

  /// Get the current local DeclContext. This is used to create closures in the
  /// right context.
  DeclContext *getCurrentDeclContext() const {
    assert(!LocalDeclContextStack.empty() && "Missing decl context");
    return LocalDeclContextStack.back();
  }

  /// Try to extract a DeclRefExpr or MemberRefExpr from the expression.
  Expr *extractDeclOrMemberRef(Expr *E) {
    while (!isa<DeclRefExpr>(E) && !isa<MemberRefExpr>(E)) {
      // TODO: Try more ways to extract interesting decl refs.
      if (auto *Subscript = dyn_cast<SubscriptExpr>(E))
        E = Subscript->getBase();
      else if (auto *InOut = dyn_cast<InOutExpr>(E))
        E = InOut->getSubExpr();
      else if (auto *MemberRef = dyn_cast<MemberRefExpr>(E))
        E = MemberRef->getBase();
      else
        return nullptr;
    }
    return E;
  }

  /// Attempt to create a functionally-equivalent replacement for OriginalExpr,
  /// given that DstExpr identifies the target of some mutating update, and that
  /// DstExpr is a subexpression of OriginalExpr.
  ///
  /// The return value contains 1) a flag indicating whether or not to
  /// recursively transform the children of the transformed expression, and 2)
  /// the transformed expression itself.
  std::pair<bool, Expr *> insertCheckExpect(Expr *OriginalExpr, Expr *DstExpr) {
    auto *DstRef = extractDeclOrMemberRef(DstExpr);
    if (!DstRef)
      return {true, OriginalExpr};

    ValueDecl *DstDecl;
    if (auto *DRE = dyn_cast<DeclRefExpr>(DstRef))
      DstDecl = DRE->getDecl();
    else {
      auto *MRE = cast<MemberRefExpr>(DstRef);
      DstDecl = MRE->getMember().getDecl();
    }
    if (!DstDecl->hasName())
      return {true, OriginalExpr};

    // Don't capture variables which aren't default-initialized.
    if (auto *VD = dyn_cast<VarDecl>(DstDecl))
      if (!VD->isParentInitialized() &&
          !(isa<ParamDecl>(VD) &&
            cast<ParamDecl>(VD)->isInOut()))
        return {true, OriginalExpr};

    // Rewrite the original expression into this:
    // call
    //   closure {
    //     $OriginalExpr
    //     checkExpect("$Varname", _stringForPrintObject($Varname))
    //   }

    // Create "$Varname".
    llvm::SmallString<256> DstNameBuf;
    const DeclName DstDN = DstDecl->getName();
    StringRef DstName = Ctx.AllocateCopy(DstDN.getString(DstNameBuf));
    assert(!DstName.empty() && "Varname must be non-empty");
    Expr *Varname = new (Ctx) StringLiteralExpr(DstName, SourceRange());
    Varname->setImplicit(true);

    // Create _stringForPrintObject($Varname).
    auto *PODeclRef = new (Ctx)
        UnresolvedDeclRefExpr(StringForPrintObjectName,
                              DeclRefKind::Ordinary, DeclNameLoc());

    std::function<DeclRefExpr *(DeclRefExpr *)> cloneDeclRef =
        [&](DeclRefExpr *DRE) {
          auto *ref = new (Ctx) DeclRefExpr(
              DRE->getDeclRef(),
              /*Loc=*/DeclNameLoc(),
              /*Implicit=*/true, DRE->getAccessSemantics(), DRE->getType());

          if (auto hopTarget = DRE->isImplicitlyAsync())
            ref->setImplicitlyAsync(*hopTarget);

          ref->setImplicitlyThrows(DRE->isImplicitlyThrows());

          return ref;
        };

    std::function<MemberRefExpr *(MemberRefExpr *)> cloneMemberRef =
        [&](MemberRefExpr *M) {
          auto *base = M->getBase();

          if (auto *DRE = dyn_cast<DeclRefExpr>(base))
            base = cloneDeclRef(DRE);
          else if (auto *M = dyn_cast<MemberRefExpr>(base))
            base = cloneMemberRef(M);

          auto *ref = new (Ctx)
              MemberRefExpr(base, /*dotLoc=*/SourceLoc(), M->getDecl(),
                            /*loc=*/DeclNameLoc(),
                            /*Implicit=*/true, M->getAccessSemantics());
          ref->setType(M->getType());

          return ref;
        };

    // Let's make a copy of either decl or member ref without source
    // information. It's invalid to have decl reference expressions
    // reused, each reference should get a fresh expression.
    if (auto *DRE = dyn_cast<DeclRefExpr>(DstRef)) {
      DstRef = cloneDeclRef(DRE);
    } else {
      DstRef = cloneMemberRef(cast<MemberRefExpr>(DstRef));
    }

    auto *POArgList = ArgumentList::forImplicitUnlabeled(Ctx, {DstRef});
    auto *POCall = CallExpr::createImplicit(Ctx, PODeclRef, POArgList);
    POCall->setThrows(false);

    // Create the call to checkExpect.
    UnresolvedDeclRefExpr *CheckExpectDRE = new (Ctx)
        UnresolvedDeclRefExpr(DebuggerTestingCheckExpectName,
                              DeclRefKind::Ordinary, DeclNameLoc());
    auto *CheckArgList =
        ArgumentList::forImplicitUnlabeled(Ctx, {Varname, POCall});
    auto *CheckExpectExpr =
        CallExpr::createImplicit(Ctx, CheckExpectDRE, CheckArgList);
    CheckExpectExpr->setThrows(false);

    // Create the closure.
    auto *Params = ParameterList::createEmpty(Ctx);
    auto *Closure = new (Ctx)
        ClosureExpr(DeclAttributes(), SourceRange(), nullptr, Params,
                    SourceLoc(), SourceLoc(),
                    SourceLoc(), SourceLoc(), nullptr,
                    DF.getNextDiscriminator(), getCurrentDeclContext());
    Closure->setImplicit(true);

    // TODO: Save and return the value of $OriginalExpr.
    ASTNode ClosureElements[] = {OriginalExpr, CheckExpectExpr};
    auto *ClosureBody = BraceStmt::create(Ctx, SourceLoc(), ClosureElements,
                                          SourceLoc(), /*Implicit=*/true);
    Closure->setBody(ClosureBody, /*isSingleExpression=*/false);

    // Call the closure.
    auto *ClosureCall = CallExpr::createImplicitEmpty(Ctx, Closure);
    ClosureCall->setThrows(false);

    // TODO: typeCheckExpression() seems to assign types to everything here,
    // but may not be sufficient in some cases.
    Expr *FinalExpr = ClosureCall;
    if (!TypeChecker::typeCheckExpression(FinalExpr, getCurrentDeclContext(),
                                          /*contextualInfo=*/{}))
      llvm::report_fatal_error("Could not type-check instrumentation");

    // Captures have to be computed after the closure is type-checked. This
    // ensures that the type checker can infer <noescape> for captured values.
    TypeChecker::computeCaptures(Closure);

    return {false, FinalExpr};
  }
};

} // end anonymous namespace

void swift::performDebuggerTestingTransform(SourceFile &SF) {
  // Walk over all decls in the file to find the next available closure
  // discriminator.
  DiscriminatorFinder DF;
  for (Decl *D : SF.getTopLevelDecls())
    D->walk(DF);

  // Instrument the decls with checkExpect() sanity-checks.
  for (Decl *D : SF.getTopLevelDecls()) {
    DebuggerTestingTransform Transform{D->getASTContext(), DF};
    D->walk(Transform);
    swift::verify(D);
  }
}
<|endoftext|>