<commit_before>/***************************************************************************
* This file is part of libmygpo-qt                                         *
* Copyright (c) 2010 Stefan Derkits <stefan@derkits.at>                    *
* Copyright (c) 2010 Christian Wagner <christian.wagner86@gmx.at>          *
* Copyright (c) 2010 Felix Winter <ixos01@gmail.com>                       *
*                                                                          *
* 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 "UrlBuilderTest.h"

using namespace mygpo;

UrlBuilderTest::UrlBuilderTest() :m_server(QLatin1String("http://gpodder.net")),
        m_api2(QLatin1String( "/api/2" )) {

}

UrlBuilderTest::~UrlBuilderTest() {

}

void UrlBuilderTest::initTestCase() {

}

void UrlBuilderTest::cleanupTestCase() {

}

void UrlBuilderTest::init() {

}

void UrlBuilderTest::cleanup() {

}

/**
 * Tests for getToplistUrl()
 */
void UrlBuilderTest::testGetToplistUrl() {
    QCOMPARE(UrlBuilder::getToplistUrl(2, UrlBuilder::JSON),
            m_server + QLatin1String("/toplist/2.json"));
    QCOMPARE(UrlBuilder::getToplistUrl(2, UrlBuilder::OPML),
            m_server + QLatin1String("/toplist/2.opml"));
}

void UrlBuilderTest::testGetToplistUrlHigh() {
    QCOMPARE(UrlBuilder::getToplistUrl(99999999, UrlBuilder::JSON),
            m_server + QLatin1String("/toplist/99999999.json"));
    QCOMPARE(UrlBuilder::getToplistUrl(99999999, UrlBuilder::OPML),
            m_server + QLatin1String("/toplist/99999999.opml"));
}

void UrlBuilderTest::testGetToplistUrlNegative() {
    QCOMPARE(UrlBuilder::getToplistUrl(-534, UrlBuilder::JSON),
            m_server + QLatin1String("/toplist/4294966762.json"));
    QCOMPARE(UrlBuilder::getToplistUrl(-534, UrlBuilder::OPML),
            m_server + QLatin1String("/toplist/4294966762.opml"));
}

void UrlBuilderTest::testGetToplistUrlZero() {
    QCOMPARE(UrlBuilder::getToplistUrl(0, UrlBuilder::JSON),
            m_server + QLatin1String("/toplist/1.json"));
    QCOMPARE(UrlBuilder::getToplistUrl(0, UrlBuilder::OPML),
            m_server + QLatin1String("/toplist/1.opml"));
}

/**
 * Tests for getSuggestionsUrl()
 */
void UrlBuilderTest::testGetSuggestionsUrl() {
    QCOMPARE(UrlBuilder::getSuggestionsUrl(2, UrlBuilder::JSON),
            m_server + QLatin1String("/suggestions/2.json"));
    QCOMPARE(UrlBuilder::getSuggestionsUrl(2, UrlBuilder::OPML),
            m_server + QLatin1String("/suggestions/2.opml"));
}

void UrlBuilderTest::testGetSuggestionsUrlHigh() {
    QCOMPARE(UrlBuilder::getSuggestionsUrl(99999999, UrlBuilder::JSON),
            m_server + QLatin1String("/suggestions/99999999.json"));
    QCOMPARE(UrlBuilder::getSuggestionsUrl(99999999, UrlBuilder::OPML),
            m_server + QLatin1String("/suggestions/99999999.opml"));
}

void UrlBuilderTest::testGetSuggestionsUrlNegative() {
    QCOMPARE(UrlBuilder::getSuggestionsUrl(-534, UrlBuilder::JSON),
            m_server + QLatin1String("/suggestions/4294966762.json"));
    QCOMPARE(UrlBuilder::getSuggestionsUrl(-534, UrlBuilder::OPML),
            m_server + QLatin1String("/suggestions/4294966762.opml"));
}

void UrlBuilderTest::testGetSuggestionsUrlZero() {
    QCOMPARE(UrlBuilder::getSuggestionsUrl(0, UrlBuilder::JSON),
            m_server + QLatin1String("/suggestions/1.json"));
    QCOMPARE(UrlBuilder::getSuggestionsUrl(0, UrlBuilder::OPML),
            m_server + QLatin1String("/suggestions/1.opml"));
}

/**
 * Tests for getPodcastSearchUrl()
 */
void UrlBuilderTest::testGetPodcastSearchUrl() {
    QCOMPARE(UrlBuilder::getPodcastSearchUrl(QLatin1String("linux"), UrlBuilder::JSON),
            m_server + QLatin1String("/search.json?q=linux"));
    QCOMPARE(UrlBuilder::getPodcastSearchUrl(QLatin1String("linux lastfm"), UrlBuilder::JSON),
            m_server + QLatin1String("/search.json?q=linux lastfm"));
    QCOMPARE(UrlBuilder::getPodcastSearchUrl(QLatin1String("linux"), UrlBuilder::OPML),
            m_server + QLatin1String("/search.opml?q=linux"));
    QCOMPARE(UrlBuilder::getPodcastSearchUrl(QLatin1String("linux lastfm"), UrlBuilder::OPML),
            m_server + QLatin1String("/search.opml?q=linux lastfm"));
}

void UrlBuilderTest::testGetPodcastSearchUrlEmpty() {
    QCOMPARE(UrlBuilder::getPodcastSearchUrl(QLatin1String(""), UrlBuilder::JSON),
            m_server + QLatin1String("/search.json?q="));
    QCOMPARE(UrlBuilder::getPodcastSearchUrl(QLatin1String(""), UrlBuilder::OPML),
            m_server + QLatin1String("/search.opml?q="));
}

void UrlBuilderTest::testGetPodcastSearchUrlSpecialCharacters() {
    QCOMPARE(UrlBuilder::getPodcastSearchUrl(QLatin1String("!\"§$%$&/()=?ß{}Ä"), UrlBuilder::JSON),
            m_server + QLatin1String("/search.json?q=!\"§$%$&/()=?ß{}Ä"));
    QCOMPARE(UrlBuilder::getPodcastSearchUrl(QLatin1String("!\"§$%$&/()=?ß{}Ä"), UrlBuilder::OPML),
            m_server + QLatin1String("/search.opml?q=!\"§$%$&/()=?ß{}Ä"));
}

/**
 * Tests for getTopTagsUrl()
 */
void UrlBuilderTest::testGetTopTagsUrl() {
    QCOMPARE(UrlBuilder::getTopTagsUrl(2),
            m_server + m_api2 + QLatin1String("/tags/2.json"));
}

void UrlBuilderTest::testGetTopTagsUrlHigh() {
    QCOMPARE(UrlBuilder::getTopTagsUrl(99999999),
            m_server + m_api2 + QLatin1String("/tags/99999999.json"));
}

void UrlBuilderTest::testGetTopTagsUrlNegative() {
    QCOMPARE(UrlBuilder::getTopTagsUrl(-534),
            m_server + m_api2 + QLatin1String("/tags/4294966762.json"));
}

void UrlBuilderTest::testGetTopTagsUrlZero() {
    QCOMPARE(UrlBuilder::getTopTagsUrl(0),
            m_server + m_api2 + QLatin1String("/tags/1.json"));
}

/**
 * Tests for getPodcastsOfTagUrl()
 */
void UrlBuilderTest::testGetPodcastsOfTagUrl() {
    QCOMPARE(UrlBuilder::getPodcastsOfTagUrl(QLatin1String("linux"), 2),
            m_server + m_api2 + QLatin1String("/tag/linux/2.json"));
    QCOMPARE(UrlBuilder::getPodcastsOfTagUrl(QLatin1String("linux lastfm"), -534),
            m_server + m_api2 + QLatin1String("/tag/linux lastfm/4294966762.json"));
}

void UrlBuilderTest::testGetPodcastsOfTagUrlEmpty() {
    QCOMPARE(UrlBuilder::getPodcastsOfTagUrl(QLatin1String(""), 2),
            m_server + m_api2 + QLatin1String("/tag//2.json"));
}

void UrlBuilderTest::testGetPodcastsOfTagUrlSpecialCharacters() {
    QCOMPARE(UrlBuilder::getPodcastsOfTagUrl(QLatin1String("!\"§$%$&/()=?ß{}Ä"), 2),
            m_server + m_api2 + QLatin1String("/tag/!\"§$%$&/()=?ß{}Ä/2.json"));
}

/**
 * Tests for getPodcastsDataUrl()
 */
void UrlBuilderTest::testGetPodcastDataUrl() {
    QCOMPARE(UrlBuilder::getPodcastDataUrl(QLatin1String("linux")),
            m_server + m_api2 + QLatin1String("/data/podcast.json?url=linux"));
    QCOMPARE(UrlBuilder::getPodcastDataUrl(QLatin1String("linux lastfm")),
            m_server + m_api2 + QLatin1String("/data/podcast.json?url=linux lastfm"));
}

void UrlBuilderTest::testGetPodcastDataUrlEmpty() {
    QCOMPARE(UrlBuilder::getPodcastDataUrl(QLatin1String("")),
            m_server + m_api2 + QLatin1String("/data/podcast.json?url="));
}

void UrlBuilderTest::testGetPodcastDataUrlSpecialCharacters() {
    QCOMPARE(UrlBuilder::getPodcastDataUrl(QLatin1String("!\"§$%$&/()=?ß{}Ä")),
            m_server + m_api2 + QLatin1String("/data/podcast.json?url=!\"§$%$&/()=?ß{}Ä"));
}

/**
 * Tests for getEpisodeDataUrl()
 */
void UrlBuilderTest::testGetEpisodeDataUrl() {
    QCOMPARE(UrlBuilder::getEpisodeDataUrl(QLatin1String("podcast1"), QLatin1String("linux")),
            m_server + m_api2 + QLatin1String("/data/episode.json?podcast=podcast1&url=linux"));
    QCOMPARE(UrlBuilder::getEpisodeDataUrl(QLatin1String("podcast2"), QLatin1String("linux lastfm")),
            m_server + m_api2 + QLatin1String("/data/episode.json?podcast=podcast2&url=linux lastfm"));
}

void UrlBuilderTest::testGetEpisodeDataUrlEmpty() {
    QCOMPARE(UrlBuilder::getEpisodeDataUrl(QLatin1String(""), QLatin1String("")),
            m_server + m_api2 + QLatin1String("/data/episode.json?podcast=&url="));
}

void UrlBuilderTest::testGetEpisodeDataUrlSpecialCharacters() {
    QCOMPARE(UrlBuilder::getEpisodeDataUrl(QLatin1String("ÖÄÜ"), QLatin1String("!\"§$%$&/()=?ß{}Ä")),
            m_server + m_api2 + QLatin1String("/data/episode.json?podcast=ÖÄÜ&url=!\"§$%$&/()=?ß{}Ä"));
}

/**
 * Tests for getFavEpisodesUrl()
 */
void UrlBuilderTest::testGetFavEpisodesUrl() {
    QCOMPARE(UrlBuilder::getFavEpisodesUrl(QLatin1String("linux")),
            m_server + m_api2 + QLatin1String("/favorites/linux.json"));
    QCOMPARE(UrlBuilder::getFavEpisodesUrl(QLatin1String("linux lastfm")),
            m_server + m_api2 + QLatin1String("/favorites/linux lastfm.json"));
}

void UrlBuilderTest::testGetFavEpisodesUrlEmpty() {
    QCOMPARE(UrlBuilder::getFavEpisodesUrl(QLatin1String("")),
            m_server + m_api2 + QLatin1String("/favorites/.json"));
}

void UrlBuilderTest::testGetFavEpisodesUrlSpecialCharacters() {
    QCOMPARE(UrlBuilder::getFavEpisodesUrl(QLatin1String("!\"§$%$&/()=?ß{}Ä")),
            m_server + m_api2 + QLatin1String("/favorites/!\"§$%$&/()=?ß{}Ä.json"));
}

/**
 * Test for getAddRemoveSubUrl()
 */
void UrlBuilderTest::testGetAddRemoveSubUrl() {
    QCOMPARE(UrlBuilder::getAddRemoveSubUrl(QLatin1String("podcast1"), QLatin1String("linux")),
            m_server + m_api2 + QLatin1String("/subscriptions/podcast1/linux.json"));
    QCOMPARE(UrlBuilder::getAddRemoveSubUrl(QLatin1String("podcast2"), QLatin1String("linux lastfm")),
            m_server + m_api2 + QLatin1String("/subscriptions/podcast2/linux lastfm.json"));
}

void UrlBuilderTest::testGetAddRemoveSubUrlEmpty() {
    QCOMPARE(UrlBuilder::getAddRemoveSubUrl(QLatin1String(""), QLatin1String("")),
            m_server + m_api2 + QLatin1String("/subscriptions//.json"));
}

void UrlBuilderTest::testGetAddRemoveSubUrlSpecialCharacters() {
    QCOMPARE(UrlBuilder::getAddRemoveSubUrl(QLatin1String("ÖÄÜ"), QLatin1String("!\"§$%$&/()=?ß{}Ä")),
            m_server + m_api2 + QLatin1String("/subscriptions/ÖÄÜ/!\"§$%$&/()=?ß{}Ä.json"));
}

/**
* Tests for getSubscriptionsUrl()
*/
void UrlBuilderTest::testGetSubscriptionUrl()
{
    QCOMPARE(UrlBuilder::getSubscriptionsUrl(QLatin1String("user123"), QLatin1String("device456"), UrlBuilder::JSON),
             m_server + QLatin1String("/subscriptions/user123/device456.json"));
}

void UrlBuilderTest::testGetSubscriptionUrlWithoutDevice()
{
    QCOMPARE(UrlBuilder::getSubscriptionsUrl(QLatin1String("user"), QString(), UrlBuilder::OPML),
             m_server + QLatin1String("/subscriptions/user.opml"));
}

QTEST_MAIN(UrlBuilderTest)
<commit_msg>adapt UrlBuilderTest to https urls<commit_after>/***************************************************************************
* This file is part of libmygpo-qt                                         *
* Copyright (c) 2010 Stefan Derkits <stefan@derkits.at>                    *
* Copyright (c) 2010 Christian Wagner <christian.wagner86@gmx.at>          *
* Copyright (c) 2010 Felix Winter <ixos01@gmail.com>                       *
*                                                                          *
* 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 "UrlBuilderTest.h"

using namespace mygpo;

UrlBuilderTest::UrlBuilderTest() :m_server(QLatin1String("https://gpodder.net")),
        m_api2(QLatin1String( "/api/2" )) {

}

UrlBuilderTest::~UrlBuilderTest() {

}

void UrlBuilderTest::initTestCase() {

}

void UrlBuilderTest::cleanupTestCase() {

}

void UrlBuilderTest::init() {

}

void UrlBuilderTest::cleanup() {

}

/**
 * Tests for getToplistUrl()
 */
void UrlBuilderTest::testGetToplistUrl() {
    QCOMPARE(UrlBuilder::getToplistUrl(2, UrlBuilder::JSON),
            m_server + QLatin1String("/toplist/2.json"));
    QCOMPARE(UrlBuilder::getToplistUrl(2, UrlBuilder::OPML),
            m_server + QLatin1String("/toplist/2.opml"));
}

void UrlBuilderTest::testGetToplistUrlHigh() {
    QCOMPARE(UrlBuilder::getToplistUrl(99999999, UrlBuilder::JSON),
            m_server + QLatin1String("/toplist/99999999.json"));
    QCOMPARE(UrlBuilder::getToplistUrl(99999999, UrlBuilder::OPML),
            m_server + QLatin1String("/toplist/99999999.opml"));
}

void UrlBuilderTest::testGetToplistUrlNegative() {
    QCOMPARE(UrlBuilder::getToplistUrl(-534, UrlBuilder::JSON),
            m_server + QLatin1String("/toplist/4294966762.json"));
    QCOMPARE(UrlBuilder::getToplistUrl(-534, UrlBuilder::OPML),
            m_server + QLatin1String("/toplist/4294966762.opml"));
}

void UrlBuilderTest::testGetToplistUrlZero() {
    QCOMPARE(UrlBuilder::getToplistUrl(0, UrlBuilder::JSON),
            m_server + QLatin1String("/toplist/1.json"));
    QCOMPARE(UrlBuilder::getToplistUrl(0, UrlBuilder::OPML),
            m_server + QLatin1String("/toplist/1.opml"));
}

/**
 * Tests for getSuggestionsUrl()
 */
void UrlBuilderTest::testGetSuggestionsUrl() {
    QCOMPARE(UrlBuilder::getSuggestionsUrl(2, UrlBuilder::JSON),
            m_server + QLatin1String("/suggestions/2.json"));
    QCOMPARE(UrlBuilder::getSuggestionsUrl(2, UrlBuilder::OPML),
            m_server + QLatin1String("/suggestions/2.opml"));
}

void UrlBuilderTest::testGetSuggestionsUrlHigh() {
    QCOMPARE(UrlBuilder::getSuggestionsUrl(99999999, UrlBuilder::JSON),
            m_server + QLatin1String("/suggestions/99999999.json"));
    QCOMPARE(UrlBuilder::getSuggestionsUrl(99999999, UrlBuilder::OPML),
            m_server + QLatin1String("/suggestions/99999999.opml"));
}

void UrlBuilderTest::testGetSuggestionsUrlNegative() {
    QCOMPARE(UrlBuilder::getSuggestionsUrl(-534, UrlBuilder::JSON),
            m_server + QLatin1String("/suggestions/4294966762.json"));
    QCOMPARE(UrlBuilder::getSuggestionsUrl(-534, UrlBuilder::OPML),
            m_server + QLatin1String("/suggestions/4294966762.opml"));
}

void UrlBuilderTest::testGetSuggestionsUrlZero() {
    QCOMPARE(UrlBuilder::getSuggestionsUrl(0, UrlBuilder::JSON),
            m_server + QLatin1String("/suggestions/1.json"));
    QCOMPARE(UrlBuilder::getSuggestionsUrl(0, UrlBuilder::OPML),
            m_server + QLatin1String("/suggestions/1.opml"));
}

/**
 * Tests for getPodcastSearchUrl()
 */
void UrlBuilderTest::testGetPodcastSearchUrl() {
    QCOMPARE(UrlBuilder::getPodcastSearchUrl(QLatin1String("linux"), UrlBuilder::JSON),
            m_server + QLatin1String("/search.json?q=linux"));
    QCOMPARE(UrlBuilder::getPodcastSearchUrl(QLatin1String("linux lastfm"), UrlBuilder::JSON),
            m_server + QLatin1String("/search.json?q=linux lastfm"));
    QCOMPARE(UrlBuilder::getPodcastSearchUrl(QLatin1String("linux"), UrlBuilder::OPML),
            m_server + QLatin1String("/search.opml?q=linux"));
    QCOMPARE(UrlBuilder::getPodcastSearchUrl(QLatin1String("linux lastfm"), UrlBuilder::OPML),
            m_server + QLatin1String("/search.opml?q=linux lastfm"));
}

void UrlBuilderTest::testGetPodcastSearchUrlEmpty() {
    QCOMPARE(UrlBuilder::getPodcastSearchUrl(QLatin1String(""), UrlBuilder::JSON),
            m_server + QLatin1String("/search.json?q="));
    QCOMPARE(UrlBuilder::getPodcastSearchUrl(QLatin1String(""), UrlBuilder::OPML),
            m_server + QLatin1String("/search.opml?q="));
}

void UrlBuilderTest::testGetPodcastSearchUrlSpecialCharacters() {
    QCOMPARE(UrlBuilder::getPodcastSearchUrl(QLatin1String("!\"§$%$&/()=?ß{}Ä"), UrlBuilder::JSON),
            m_server + QLatin1String("/search.json?q=!\"§$%$&/()=?ß{}Ä"));
    QCOMPARE(UrlBuilder::getPodcastSearchUrl(QLatin1String("!\"§$%$&/()=?ß{}Ä"), UrlBuilder::OPML),
            m_server + QLatin1String("/search.opml?q=!\"§$%$&/()=?ß{}Ä"));
}

/**
 * Tests for getTopTagsUrl()
 */
void UrlBuilderTest::testGetTopTagsUrl() {
    QCOMPARE(UrlBuilder::getTopTagsUrl(2),
            m_server + m_api2 + QLatin1String("/tags/2.json"));
}

void UrlBuilderTest::testGetTopTagsUrlHigh() {
    QCOMPARE(UrlBuilder::getTopTagsUrl(99999999),
            m_server + m_api2 + QLatin1String("/tags/99999999.json"));
}

void UrlBuilderTest::testGetTopTagsUrlNegative() {
    QCOMPARE(UrlBuilder::getTopTagsUrl(-534),
            m_server + m_api2 + QLatin1String("/tags/4294966762.json"));
}

void UrlBuilderTest::testGetTopTagsUrlZero() {
    QCOMPARE(UrlBuilder::getTopTagsUrl(0),
            m_server + m_api2 + QLatin1String("/tags/1.json"));
}

/**
 * Tests for getPodcastsOfTagUrl()
 */
void UrlBuilderTest::testGetPodcastsOfTagUrl() {
    QCOMPARE(UrlBuilder::getPodcastsOfTagUrl(QLatin1String("linux"), 2),
            m_server + m_api2 + QLatin1String("/tag/linux/2.json"));
    QCOMPARE(UrlBuilder::getPodcastsOfTagUrl(QLatin1String("linux lastfm"), -534),
            m_server + m_api2 + QLatin1String("/tag/linux lastfm/4294966762.json"));
}

void UrlBuilderTest::testGetPodcastsOfTagUrlEmpty() {
    QCOMPARE(UrlBuilder::getPodcastsOfTagUrl(QLatin1String(""), 2),
            m_server + m_api2 + QLatin1String("/tag//2.json"));
}

void UrlBuilderTest::testGetPodcastsOfTagUrlSpecialCharacters() {
    QCOMPARE(UrlBuilder::getPodcastsOfTagUrl(QLatin1String("!\"§$%$&/()=?ß{}Ä"), 2),
            m_server + m_api2 + QLatin1String("/tag/!\"§$%$&/()=?ß{}Ä/2.json"));
}

/**
 * Tests for getPodcastsDataUrl()
 */
void UrlBuilderTest::testGetPodcastDataUrl() {
    QCOMPARE(UrlBuilder::getPodcastDataUrl(QLatin1String("linux")),
            m_server + m_api2 + QLatin1String("/data/podcast.json?url=linux"));
    QCOMPARE(UrlBuilder::getPodcastDataUrl(QLatin1String("linux lastfm")),
            m_server + m_api2 + QLatin1String("/data/podcast.json?url=linux lastfm"));
}

void UrlBuilderTest::testGetPodcastDataUrlEmpty() {
    QCOMPARE(UrlBuilder::getPodcastDataUrl(QLatin1String("")),
            m_server + m_api2 + QLatin1String("/data/podcast.json?url="));
}

void UrlBuilderTest::testGetPodcastDataUrlSpecialCharacters() {
    QCOMPARE(UrlBuilder::getPodcastDataUrl(QLatin1String("!\"§$%$&/()=?ß{}Ä")),
            m_server + m_api2 + QLatin1String("/data/podcast.json?url=!\"§$%$&/()=?ß{}Ä"));
}

/**
 * Tests for getEpisodeDataUrl()
 */
void UrlBuilderTest::testGetEpisodeDataUrl() {
    QCOMPARE(UrlBuilder::getEpisodeDataUrl(QLatin1String("podcast1"), QLatin1String("linux")),
            m_server + m_api2 + QLatin1String("/data/episode.json?podcast=podcast1&url=linux"));
    QCOMPARE(UrlBuilder::getEpisodeDataUrl(QLatin1String("podcast2"), QLatin1String("linux lastfm")),
            m_server + m_api2 + QLatin1String("/data/episode.json?podcast=podcast2&url=linux lastfm"));
}

void UrlBuilderTest::testGetEpisodeDataUrlEmpty() {
    QCOMPARE(UrlBuilder::getEpisodeDataUrl(QLatin1String(""), QLatin1String("")),
            m_server + m_api2 + QLatin1String("/data/episode.json?podcast=&url="));
}

void UrlBuilderTest::testGetEpisodeDataUrlSpecialCharacters() {
    QCOMPARE(UrlBuilder::getEpisodeDataUrl(QLatin1String("ÖÄÜ"), QLatin1String("!\"§$%$&/()=?ß{}Ä")),
            m_server + m_api2 + QLatin1String("/data/episode.json?podcast=ÖÄÜ&url=!\"§$%$&/()=?ß{}Ä"));
}

/**
 * Tests for getFavEpisodesUrl()
 */
void UrlBuilderTest::testGetFavEpisodesUrl() {
    QCOMPARE(UrlBuilder::getFavEpisodesUrl(QLatin1String("linux")),
            m_server + m_api2 + QLatin1String("/favorites/linux.json"));
    QCOMPARE(UrlBuilder::getFavEpisodesUrl(QLatin1String("linux lastfm")),
            m_server + m_api2 + QLatin1String("/favorites/linux lastfm.json"));
}

void UrlBuilderTest::testGetFavEpisodesUrlEmpty() {
    QCOMPARE(UrlBuilder::getFavEpisodesUrl(QLatin1String("")),
            m_server + m_api2 + QLatin1String("/favorites/.json"));
}

void UrlBuilderTest::testGetFavEpisodesUrlSpecialCharacters() {
    QCOMPARE(UrlBuilder::getFavEpisodesUrl(QLatin1String("!\"§$%$&/()=?ß{}Ä")),
            m_server + m_api2 + QLatin1String("/favorites/!\"§$%$&/()=?ß{}Ä.json"));
}

/**
 * Test for getAddRemoveSubUrl()
 */
void UrlBuilderTest::testGetAddRemoveSubUrl() {
    QCOMPARE(UrlBuilder::getAddRemoveSubUrl(QLatin1String("podcast1"), QLatin1String("linux")),
            m_server + m_api2 + QLatin1String("/subscriptions/podcast1/linux.json"));
    QCOMPARE(UrlBuilder::getAddRemoveSubUrl(QLatin1String("podcast2"), QLatin1String("linux lastfm")),
            m_server + m_api2 + QLatin1String("/subscriptions/podcast2/linux lastfm.json"));
}

void UrlBuilderTest::testGetAddRemoveSubUrlEmpty() {
    QCOMPARE(UrlBuilder::getAddRemoveSubUrl(QLatin1String(""), QLatin1String("")),
            m_server + m_api2 + QLatin1String("/subscriptions//.json"));
}

void UrlBuilderTest::testGetAddRemoveSubUrlSpecialCharacters() {
    QCOMPARE(UrlBuilder::getAddRemoveSubUrl(QLatin1String("ÖÄÜ"), QLatin1String("!\"§$%$&/()=?ß{}Ä")),
            m_server + m_api2 + QLatin1String("/subscriptions/ÖÄÜ/!\"§$%$&/()=?ß{}Ä.json"));
}

/**
* Tests for getSubscriptionsUrl()
*/
void UrlBuilderTest::testGetSubscriptionUrl()
{
    QCOMPARE(UrlBuilder::getSubscriptionsUrl(QLatin1String("user123"), QLatin1String("device456"), UrlBuilder::JSON),
             m_server + QLatin1String("/subscriptions/user123/device456.json"));
}

void UrlBuilderTest::testGetSubscriptionUrlWithoutDevice()
{
    QCOMPARE(UrlBuilder::getSubscriptionsUrl(QLatin1String("user"), QString(), UrlBuilder::OPML),
             m_server + QLatin1String("/subscriptions/user.opml"));
}

QTEST_MAIN(UrlBuilderTest)
<|endoftext|>


<commit_before>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of libmeegotouch.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

// TODO: Consider calculating size hint from the content

#include "mbanner.h"
#include "mbannerview.h"
#include "mbannerview_p.h"

#include "mviewcreator.h"
#include "mimagewidget.h"
#include "mlabel.h"

#include "mlayout.h"
#include "mlinearlayoutpolicy.h"
#include "mgridlayoutpolicy.h"

#include <QGraphicsSceneMouseEvent>

MBannerViewPrivate::MBannerViewPrivate() :
    layout(0),
    landscapePolicy(0),
    icon(0),
    title(0),
    subtitle(0),
    controller(controller)
{
}

MBannerViewPrivate::~MBannerViewPrivate()
{
}

void MBannerViewPrivate::initDynamicLayout()
{
    layout = new MLayout();

    landscapePolicy = new MLinearLayoutPolicy(layout, Qt::Horizontal);

    landscapePolicy->setSpacing(0);
    landscapePolicy->setContentsMargins(0, 0, 0, 0);

    portraitPolicy = new MGridLayoutPolicy(layout);
    portraitPolicy->setContentsMargins(0, 0, 0, 0);
    portraitPolicy->setSpacing(0);

    Q_Q(MBannerView);

    if(!q->model()->iconID().isEmpty() && !q->model()->title().isEmpty()
        && !q->model()->subtitle().isEmpty()){

        icon = new MImageWidget();
        icon->setObjectName("CommonBannerMainIcon");
        landscapePolicy->addItem(icon,Qt::AlignVCenter);
        portraitPolicy->addItem(icon,0,0,2,1, Qt::AlignVCenter );

        title = new MLabel();
        title->setObjectName("CommonBannerSingleTitle");
        title->setAlignment(Qt::AlignVCenter);
        title->setWordWrap(true);
        landscapePolicy->addItem(title);
        portraitPolicy->addItem(title, 0,1,Qt::AlignLeft);

        subtitle = new MLabel();
        subtitle->setObjectName("CommonBannerSingleSubTitle");
        subtitle->setAlignment(Qt::AlignVCenter);
        subtitle->setWordWrap(true);
        landscapePolicy->addItem(subtitle);
        portraitPolicy->addItem(subtitle,1,1,Qt::AlignVCenter);

     }else if(q->model()->iconID().isEmpty() && !q->model()->subtitle().isEmpty()){

        subtitle = new MLabel();
        subtitle->setObjectName("CommonBannerSingleSubTitle");
        subtitle->setAlignment(Qt::AlignVCenter);
        subtitle->setWordWrap(true);
        landscapePolicy->addItem(subtitle);
        portraitPolicy->addItem(subtitle,0,0,Qt::AlignVCenter);

    }else if(!q->model()->iconID().isEmpty() && !q->model()->subtitle().isEmpty()){

        icon = new MImageWidget();
        icon->setObjectName("CommonBannerMainIcon");
        landscapePolicy->addItem(icon,Qt::AlignVCenter);
        portraitPolicy->addItem(icon,0,0,2,1, Qt::AlignVCenter );

        subtitle = new MLabel();
        subtitle->setObjectName("CommonBannerSingleSubTitle");
        subtitle->setAlignment(Qt::AlignVCenter);
        subtitle->setWordWrap(true);
        landscapePolicy->addItem(subtitle);
        portraitPolicy->addItem(subtitle,1,1,Qt::AlignVCenter);
    }

    landscapePolicy->addStretch();

    layout->setPortraitPolicy(portraitPolicy);
    layout->setLandscapePolicy(landscapePolicy);

    controller->setLayout(layout);

    QObject::connect(controller, SIGNAL(clicked()), controller, SLOT(dismiss()));
}

MBannerView::MBannerView(MBanner *controller) :
    MSceneWindowView(controller),
    d_ptr(new MBannerViewPrivate)
{
    Q_D(MBannerView);
    d->q_ptr = this;
    d->controller = controller;
}

MBannerView::MBannerView(MBannerViewPrivate &dd, MBanner *controller) :
    MSceneWindowView(controller),
    d_ptr(&dd)
{
    Q_D(MBannerView);
    d->q_ptr = this;
    d->controller = controller;
}

MBannerView::~MBannerView()
{
    delete d_ptr;
}

void MBannerView::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    Q_UNUSED(event);
}

void MBannerView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    Q_D(MBannerView);

     QPointF touch = event->scenePos();
     QRectF rect  = d->controller->sceneBoundingRect();

     if (rect.contains(touch)) {
         d->controller->click();
     }
}

void MBannerView::setupModel()
{
    MSceneWindowView::setupModel();

    Q_D(MBannerView);

    d->initDynamicLayout();

    if (!model()->title().isEmpty())
        d->title->setText(model()->title());
    if (!model()->subtitle().isEmpty())
        d->subtitle->setText(model()->subtitle());
    if (!model()->iconID().isEmpty())
        d->icon->setImage(model()->iconID(), style()->iconSize());

}

void MBannerView::applyStyle()
{
    MSceneWindowView::applyStyle();
}

void MBannerView::updateData(const QList<const char *>& modifications)
{
    MSceneWindowView::updateData(modifications);

    Q_D(MBannerView);

    d->initDynamicLayout();

    const char *member;

    foreach(member, modifications) {
        if (member == MBannerModel::Title){
            d->title->setText(model()->title());
        }else if (member == MBannerModel::Subtitle) {
            d->subtitle->setText(model()->subtitle());
        }else if (member == MBannerModel::IconID) {
            d->icon->setImage(model()->iconID(), style()->iconSize());
        }
    }
}

M_REGISTER_VIEW_NEW(MBannerView, MBanner)
<commit_msg>fix coverity id 909<commit_after>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of libmeegotouch.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

// TODO: Consider calculating size hint from the content

#include "mbanner.h"
#include "mbannerview.h"
#include "mbannerview_p.h"

#include "mviewcreator.h"
#include "mimagewidget.h"
#include "mlabel.h"

#include "mlayout.h"
#include "mlinearlayoutpolicy.h"
#include "mgridlayoutpolicy.h"

#include <QGraphicsSceneMouseEvent>

MBannerViewPrivate::MBannerViewPrivate() :
    layout(0),
    landscapePolicy(0),
    icon(0),
    title(0),
    subtitle(0)
{
}

MBannerViewPrivate::~MBannerViewPrivate()
{
}

void MBannerViewPrivate::initDynamicLayout()
{
    layout = new MLayout();

    landscapePolicy = new MLinearLayoutPolicy(layout, Qt::Horizontal);

    landscapePolicy->setSpacing(0);
    landscapePolicy->setContentsMargins(0, 0, 0, 0);

    portraitPolicy = new MGridLayoutPolicy(layout);
    portraitPolicy->setContentsMargins(0, 0, 0, 0);
    portraitPolicy->setSpacing(0);

    Q_Q(MBannerView);

    if(!q->model()->iconID().isEmpty() && !q->model()->title().isEmpty()
        && !q->model()->subtitle().isEmpty()){

        icon = new MImageWidget();
        icon->setObjectName("CommonBannerMainIcon");
        landscapePolicy->addItem(icon,Qt::AlignVCenter);
        portraitPolicy->addItem(icon,0,0,2,1, Qt::AlignVCenter );

        title = new MLabel();
        title->setObjectName("CommonBannerSingleTitle");
        title->setAlignment(Qt::AlignVCenter);
        title->setWordWrap(true);
        landscapePolicy->addItem(title);
        portraitPolicy->addItem(title, 0,1,Qt::AlignLeft);

        subtitle = new MLabel();
        subtitle->setObjectName("CommonBannerSingleSubTitle");
        subtitle->setAlignment(Qt::AlignVCenter);
        subtitle->setWordWrap(true);
        landscapePolicy->addItem(subtitle);
        portraitPolicy->addItem(subtitle,1,1,Qt::AlignVCenter);

     }else if(q->model()->iconID().isEmpty() && !q->model()->subtitle().isEmpty()){

        subtitle = new MLabel();
        subtitle->setObjectName("CommonBannerSingleSubTitle");
        subtitle->setAlignment(Qt::AlignVCenter);
        subtitle->setWordWrap(true);
        landscapePolicy->addItem(subtitle);
        portraitPolicy->addItem(subtitle,0,0,Qt::AlignVCenter);

    }else if(!q->model()->iconID().isEmpty() && !q->model()->subtitle().isEmpty()){

        icon = new MImageWidget();
        icon->setObjectName("CommonBannerMainIcon");
        landscapePolicy->addItem(icon,Qt::AlignVCenter);
        portraitPolicy->addItem(icon,0,0,2,1, Qt::AlignVCenter );

        subtitle = new MLabel();
        subtitle->setObjectName("CommonBannerSingleSubTitle");
        subtitle->setAlignment(Qt::AlignVCenter);
        subtitle->setWordWrap(true);
        landscapePolicy->addItem(subtitle);
        portraitPolicy->addItem(subtitle,1,1,Qt::AlignVCenter);
    }

    landscapePolicy->addStretch();

    layout->setPortraitPolicy(portraitPolicy);
    layout->setLandscapePolicy(landscapePolicy);

    controller->setLayout(layout);

    QObject::connect(controller, SIGNAL(clicked()), controller, SLOT(dismiss()));
}

MBannerView::MBannerView(MBanner *controller) :
    MSceneWindowView(controller),
    d_ptr(new MBannerViewPrivate)
{
    Q_D(MBannerView);
    d->q_ptr = this;
    d->controller = controller;
}

MBannerView::MBannerView(MBannerViewPrivate &dd, MBanner *controller) :
    MSceneWindowView(controller),
    d_ptr(&dd)
{
    Q_D(MBannerView);
    d->q_ptr = this;
    d->controller = controller;
}

MBannerView::~MBannerView()
{
    delete d_ptr;
}

void MBannerView::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    Q_UNUSED(event);
}

void MBannerView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    Q_D(MBannerView);

     QPointF touch = event->scenePos();
     QRectF rect  = d->controller->sceneBoundingRect();

     if (rect.contains(touch)) {
         d->controller->click();
     }
}

void MBannerView::setupModel()
{
    MSceneWindowView::setupModel();

    Q_D(MBannerView);

    d->initDynamicLayout();

    if (!model()->title().isEmpty())
        d->title->setText(model()->title());
    if (!model()->subtitle().isEmpty())
        d->subtitle->setText(model()->subtitle());
    if (!model()->iconID().isEmpty())
        d->icon->setImage(model()->iconID(), style()->iconSize());

}

void MBannerView::applyStyle()
{
    MSceneWindowView::applyStyle();
}

void MBannerView::updateData(const QList<const char *>& modifications)
{
    MSceneWindowView::updateData(modifications);

    Q_D(MBannerView);

    d->initDynamicLayout();

    const char *member;

    foreach(member, modifications) {
        if (member == MBannerModel::Title){
            d->title->setText(model()->title());
        }else if (member == MBannerModel::Subtitle) {
            d->subtitle->setText(model()->subtitle());
        }else if (member == MBannerModel::IconID) {
            d->icon->setImage(model()->iconID(), style()->iconSize());
        }
    }
}

M_REGISTER_VIEW_NEW(MBannerView, MBanner)
<|endoftext|>


<commit_before>void read_from_file(){

    // Let's open the TFile
    TFile in_file("my_rootfile.root");

    // Get the Histogram out
    TH1F* h;
    in_file.GetObject("my_histogram",h);

    // Draw it
    h->Draw();
}
<commit_msg>Adapt to ROOT6<commit_after>void read_from_file(){

    // Let's open the TFile
    TFile in_file("my_rootfile.root");

    // Get the Histogram out
    TH1F* h;
    in_file.GetObject("my_histogram",h);

    // Draw it
    auto myCanvas = new TCanvas();
    h->Draw();
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: xmllib_imexp.hxx,v $
 *
 *  $Revision: 1.9 $
 *
 *  last change: $Author: obo $ $Date: 2004-03-17 13:42:25 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  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., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/
#ifndef _XMLSCRIPT_XMLLIB_IMEXP_HXX_
#define _XMLSCRIPT_XMLLIB_IMEXP_HXX_

#ifndef _COM_SUN_STAR_XML_SAX_XEXTENDEDDOCUMENTHANDLER_HPP_
#include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp>
#endif
#ifndef _COM_SUN_STAR_UNO_SEQUENCE_HXX_
#include <com/sun/star/uno/Sequence.hxx>
#endif

#include "xmlscript/xmlns.h"

namespace xmlscript
{

//==============================================================================
// Library container export
// HACK C++ struct to transport info. Later the container
// itself should do the export/import and use exportet XML
// functionality from xmlscript
struct LibDescriptor
{
    ::rtl::OUString aName;
    ::rtl::OUString aStorageURL;
    sal_Bool bLink;
    sal_Bool bReadOnly;
    sal_Bool bPasswordProtected;
    ::com::sun::star::uno::Sequence< ::rtl::OUString > aElementNames;
    sal_Bool bPreload;
};

struct LibDescriptorArray
{
    LibDescriptor* mpLibs;
    sal_Int32 mnLibCount;

    LibDescriptorArray( void ) { mpLibs = NULL; mnLibCount = 0; }
    LibDescriptorArray( sal_Int32 nLibCount );

    ~LibDescriptorArray();

};

SAL_DLLEXPORT void
SAL_CALL exportLibraryContainer(
    ::com::sun::star::uno::Reference<
    ::com::sun::star::xml::sax::XExtendedDocumentHandler > const & xOut,
    const LibDescriptorArray* pLibArray )
        SAL_THROW( (::com::sun::star::uno::Exception) );

SAL_DLLEXPORT ::com::sun::star::uno::Reference<
    ::com::sun::star::xml::sax::XDocumentHandler >
SAL_CALL importLibraryContainer( LibDescriptorArray* pLibArray )
        SAL_THROW( (::com::sun::star::uno::Exception) );

SAL_DLLEXPORT void
SAL_CALL exportLibrary(
    ::com::sun::star::uno::Reference<
    ::com::sun::star::xml::sax::XExtendedDocumentHandler > const & xOut,
    const LibDescriptor& rLib )
        SAL_THROW( (::com::sun::star::uno::Exception) );

SAL_DLLEXPORT ::com::sun::star::uno::Reference<
    ::com::sun::star::xml::sax::XDocumentHandler >
SAL_CALL importLibrary( LibDescriptor& rLib )
        SAL_THROW( (::com::sun::star::uno::Exception) );

}

#endif
<commit_msg>INTEGRATION: CWS tune03 (1.9.22); FILE MERGED 2004/07/22 19:07:38 mhu 1.9.22.1: #i29979# Removed usage of SAL_DLLEXPORT (see @ sal/types.h)<commit_after>/*************************************************************************
 *
 *  $RCSfile: xmllib_imexp.hxx,v $
 *
 *  $Revision: 1.10 $
 *
 *  last change: $Author: rt $ $Date: 2004-08-23 09:14:09 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  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., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/
#ifndef _XMLSCRIPT_XMLLIB_IMEXP_HXX_
#define _XMLSCRIPT_XMLLIB_IMEXP_HXX_

#ifndef _COM_SUN_STAR_XML_SAX_XEXTENDEDDOCUMENTHANDLER_HPP_
#include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp>
#endif
#ifndef _COM_SUN_STAR_UNO_SEQUENCE_HXX_
#include <com/sun/star/uno/Sequence.hxx>
#endif

#include "xmlscript/xmlns.h"

namespace xmlscript
{

//==============================================================================
// Library container export
// HACK C++ struct to transport info. Later the container
// itself should do the export/import and use exportet XML
// functionality from xmlscript
struct LibDescriptor
{
    ::rtl::OUString aName;
    ::rtl::OUString aStorageURL;
    sal_Bool bLink;
    sal_Bool bReadOnly;
    sal_Bool bPasswordProtected;
    ::com::sun::star::uno::Sequence< ::rtl::OUString > aElementNames;
    sal_Bool bPreload;
};

struct LibDescriptorArray
{
    LibDescriptor* mpLibs;
    sal_Int32 mnLibCount;

    LibDescriptorArray( void ) { mpLibs = NULL; mnLibCount = 0; }
    LibDescriptorArray( sal_Int32 nLibCount );

    ~LibDescriptorArray();

};

void
SAL_CALL exportLibraryContainer(
    ::com::sun::star::uno::Reference<
    ::com::sun::star::xml::sax::XExtendedDocumentHandler > const & xOut,
    const LibDescriptorArray* pLibArray )
        SAL_THROW( (::com::sun::star::uno::Exception) );

::com::sun::star::uno::Reference<
    ::com::sun::star::xml::sax::XDocumentHandler >
SAL_CALL importLibraryContainer( LibDescriptorArray* pLibArray )
        SAL_THROW( (::com::sun::star::uno::Exception) );

void
SAL_CALL exportLibrary(
    ::com::sun::star::uno::Reference<
    ::com::sun::star::xml::sax::XExtendedDocumentHandler > const & xOut,
    const LibDescriptor& rLib )
        SAL_THROW( (::com::sun::star::uno::Exception) );

::com::sun::star::uno::Reference<
    ::com::sun::star::xml::sax::XDocumentHandler >
SAL_CALL importLibrary( LibDescriptor& rLib )
        SAL_THROW( (::com::sun::star::uno::Exception) );

}

#endif
<|endoftext|>


<commit_before>/*
* Copyright (c) 2013-2015 Fredrik Mellbin
*
* This file is part of VapourSynth.
*
* VapourSynth 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.
*
* VapourSynth 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 VapourSynth; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "VapourSynth.h"
#include "VSScript.h"
#include "cython/vapoursynth_api.h"
#include <mutex>
#include <atomic>

#ifdef VS_TARGET_OS_WINDOWS
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#endif

std::once_flag flag;

struct VSScript : public VPYScriptExport {
};

std::atomic<int> initializationCount(0);
std::atomic<int> scriptId(1000);
bool initialized = false;
PyThreadState *ts = nullptr;
PyGILState_STATE s;

static void real_init(void) {
#ifdef VS_TARGET_OS_WINDOWS

#if defined(_WIN32) && defined(_WIN64)
    const wchar_t *keyPython = L"SOFTWARE\\Python\\PythonCore\\3.5\\InstallPath";
#elif defined(_WIN32) && !defined(_WIN64)
    const wchar_t *keyPython = L"SOFTWARE\\Python\\PythonCore\\3.5-32\\InstallPath";
#else
#error "Cannot determine Compiler Bitdepth - is this really Windows?"
#endif

    DWORD dwType = REG_SZ;
    HKEY hKey = 0;

    wchar_t value[1024];
    DWORD valueLength = 1000;
    if (RegOpenKeyW(HKEY_CURRENT_USER, keyPython, &hKey) != ERROR_SUCCESS
        && RegOpenKeyW(HKEY_LOCAL_MACHINE, keyPython, &hKey) != ERROR_SUCCESS)
        return;
    LSTATUS status = RegQueryValueExW(hKey, L"", nullptr, &dwType, (LPBYTE)&value, &valueLength);
    RegCloseKey(hKey);
    if (status != ERROR_SUCCESS)
        return;

    std::wstring pyPath = value;
    pyPath += L"python35.dll";

    HMODULE pythonDll = LoadLibraryW(pyPath.c_str());
    if (!pythonDll)
        return;
#endif
    int preInitialized = Py_IsInitialized();
    if (!preInitialized)
        Py_InitializeEx(0);
    s = PyGILState_Ensure();
    if (import_vapoursynth())
        return;
    if (vpy_initVSScript())
        return;
    ts = PyEval_SaveThread();
    initialized = true;
}

VS_API(int) vsscript_init() {
    std::call_once(flag, real_init);
    if (initialized)
        return ++initializationCount;
    else
        return initializationCount;
}

VS_API(int) vsscript_finalize(void) {
    int count = --initializationCount;
    assert(count >= 0);
    return count;
}

VS_API(int) vsscript_createScript(VSScript **handle) {
    *handle = new(std::nothrow)VSScript();
    if (*handle) {
        (*handle)->pyenvdict = nullptr;
        (*handle)->errstr = nullptr;
        (*handle)->id = ++scriptId;
        return vpy_createScript(*handle);
    } else {
        return 1;
    }
}

VS_API(int) vsscript_evaluateScript(VSScript **handle, const char *script, const char *scriptFilename, int flags) {
    if (*handle == nullptr) {
        *handle = new(std::nothrow)VSScript();
        if (*handle) {
            (*handle)->pyenvdict = nullptr;
            (*handle)->errstr = nullptr;
            (*handle)->id = ++scriptId;
        } else {
            return 1;
        }
    }
    return vpy_evaluateScript(*handle, script, scriptFilename ? scriptFilename : "<string>", flags);
}

VS_API(int) vsscript_evaluateFile(VSScript **handle, const char *scriptFilename, int flags) {
    if (*handle == nullptr) {
        *handle = new(std::nothrow)VSScript();
        if (*handle) {
            (*handle)->pyenvdict = nullptr;
            (*handle)->errstr = nullptr;
            (*handle)->id = ++scriptId;
        } else {
            return 1;
        }
    }
    return vpy_evaluateFile(*handle, scriptFilename, flags);
}

VS_API(void) vsscript_freeScript(VSScript *handle) {
    if (handle) {
        vpy_freeScript(handle);
        delete handle;
    }
}

VS_API(const char *) vsscript_getError(VSScript *handle) {
    if (handle)
        return vpy_getError(handle);
    else
        return "Invalid handle (NULL)";
}

VS_API(VSNodeRef *) vsscript_getOutput(VSScript *handle, int index) {
    return vpy_getOutput(handle, index);
}

VS_API(int) vsscript_clearOutput(VSScript *handle, int index) {
    return vpy_clearOutput(handle, index);
}

VS_API(VSCore *) vsscript_getCore(VSScript *handle) {
    return vpy_getCore(handle);
}

VS_API(const VSAPI *) vsscript_getVSApi(void) {
    return vpy_getVSApi();
}

VS_API(int) vsscript_getVariable(VSScript *handle, const char *name, VSMap *dst) {
    return vpy_getVariable(handle, name, dst);
}

VS_API(int) vsscript_setVariable(VSScript *handle, const VSMap *vars) {
    return vpy_setVariable(handle, vars);
}

VS_API(int) vsscript_clearVariable(VSScript *handle, const char *name) {
    return vpy_clearVariable(handle, name);
}

VS_API(void) vsscript_clearEnvironment(VSScript *handle) {
    vpy_clearEnvironment(handle);
}
<commit_msg>Make vsscript look for python dll in normal search path first<commit_after>/*
* Copyright (c) 2013-2016 Fredrik Mellbin
*
* This file is part of VapourSynth.
*
* VapourSynth 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.
*
* VapourSynth 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 VapourSynth; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "VapourSynth.h"
#include "VSScript.h"
#include "cython/vapoursynth_api.h"
#include <mutex>
#include <atomic>

#ifdef VS_TARGET_OS_WINDOWS
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#endif

std::once_flag flag;

struct VSScript : public VPYScriptExport {
};

std::atomic<int> initializationCount(0);
std::atomic<int> scriptId(1000);
bool initialized = false;
PyThreadState *ts = nullptr;
PyGILState_STATE s;

static void real_init(void) {
#ifdef VS_TARGET_OS_WINDOWS

#if defined(_WIN32) && defined(_WIN64)
    const wchar_t *keyPython = L"SOFTWARE\\Python\\PythonCore\\3.5\\InstallPath";
#elif defined(_WIN32) && !defined(_WIN64)
    const wchar_t *keyPython = L"SOFTWARE\\Python\\PythonCore\\3.5-32\\InstallPath";
#else
#error "Cannot determine Compiler Bitdepth - is this really Windows?"
#endif

    const std::wstring pythonDllName = L"python35.dll";
    HMODULE pythonDll = LoadLibraryW(pythonDllName.c_str());
    if (!pythonDll) {
        DWORD dwType = REG_SZ;
        HKEY hKey = 0;

        wchar_t value[1024];
        DWORD valueLength = 1000;
        if (RegOpenKeyW(HKEY_CURRENT_USER, keyPython, &hKey) != ERROR_SUCCESS
            && RegOpenKeyW(HKEY_LOCAL_MACHINE, keyPython, &hKey) != ERROR_SUCCESS)
            return;
        LSTATUS status = RegQueryValueExW(hKey, L"", nullptr, &dwType, (LPBYTE)&value, &valueLength);
        RegCloseKey(hKey);
        if (status != ERROR_SUCCESS)
            return;

        std::wstring pyPath = value;
        pyPath += L"python35.dll";

        pythonDll = LoadLibraryW(pyPath.c_str());
        if (!pythonDll)
            return;
    }

#endif
    int preInitialized = Py_IsInitialized();
    if (!preInitialized)
        Py_InitializeEx(0);
    s = PyGILState_Ensure();
    if (import_vapoursynth())
        return;
    if (vpy_initVSScript())
        return;
    ts = PyEval_SaveThread();
    initialized = true;
}

VS_API(int) vsscript_init() {
    std::call_once(flag, real_init);
    if (initialized)
        return ++initializationCount;
    else
        return initializationCount;
}

VS_API(int) vsscript_finalize(void) {
    int count = --initializationCount;
    assert(count >= 0);
    return count;
}

VS_API(int) vsscript_createScript(VSScript **handle) {
    *handle = new(std::nothrow)VSScript();
    if (*handle) {
        (*handle)->pyenvdict = nullptr;
        (*handle)->errstr = nullptr;
        (*handle)->id = ++scriptId;
        return vpy_createScript(*handle);
    } else {
        return 1;
    }
}

VS_API(int) vsscript_evaluateScript(VSScript **handle, const char *script, const char *scriptFilename, int flags) {
    if (*handle == nullptr) {
        *handle = new(std::nothrow)VSScript();
        if (*handle) {
            (*handle)->pyenvdict = nullptr;
            (*handle)->errstr = nullptr;
            (*handle)->id = ++scriptId;
        } else {
            return 1;
        }
    }
    return vpy_evaluateScript(*handle, script, scriptFilename ? scriptFilename : "<string>", flags);
}

VS_API(int) vsscript_evaluateFile(VSScript **handle, const char *scriptFilename, int flags) {
    if (*handle == nullptr) {
        *handle = new(std::nothrow)VSScript();
        if (*handle) {
            (*handle)->pyenvdict = nullptr;
            (*handle)->errstr = nullptr;
            (*handle)->id = ++scriptId;
        } else {
            return 1;
        }
    }
    return vpy_evaluateFile(*handle, scriptFilename, flags);
}

VS_API(void) vsscript_freeScript(VSScript *handle) {
    if (handle) {
        vpy_freeScript(handle);
        delete handle;
    }
}

VS_API(const char *) vsscript_getError(VSScript *handle) {
    if (handle)
        return vpy_getError(handle);
    else
        return "Invalid handle (NULL)";
}

VS_API(VSNodeRef *) vsscript_getOutput(VSScript *handle, int index) {
    return vpy_getOutput(handle, index);
}

VS_API(int) vsscript_clearOutput(VSScript *handle, int index) {
    return vpy_clearOutput(handle, index);
}

VS_API(VSCore *) vsscript_getCore(VSScript *handle) {
    return vpy_getCore(handle);
}

VS_API(const VSAPI *) vsscript_getVSApi(void) {
    return vpy_getVSApi();
}

VS_API(int) vsscript_getVariable(VSScript *handle, const char *name, VSMap *dst) {
    return vpy_getVariable(handle, name, dst);
}

VS_API(int) vsscript_setVariable(VSScript *handle, const VSMap *vars) {
    return vpy_setVariable(handle, vars);
}

VS_API(int) vsscript_clearVariable(VSScript *handle, const char *name) {
    return vpy_clearVariable(handle, name);
}

VS_API(void) vsscript_clearEnvironment(VSScript *handle) {
    vpy_clearEnvironment(handle);
}
<|endoftext|>


<commit_before>/*************************************************
* Unix EntropySource Source File                 *
* (C) 1999-2008 Jack Lloyd                       *
*************************************************/

#include <botan/es_unix.h>
#include <botan/unix_cmd.h>
#include <botan/parsing.h>
#include <botan/xor_buf.h>
#include <algorithm>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <unistd.h>

namespace Botan {

namespace {

/*************************************************
* Sort ordering by priority                      *
*************************************************/
bool Unix_Program_Cmp(const Unix_Program& a, const Unix_Program& b)
   { return (a.priority < b.priority); }

}

/*************************************************
* Unix_EntropySource Constructor                 *
*************************************************/
Unix_EntropySource::Unix_EntropySource(const std::vector<std::string>& path) :
   PATH(path)
   {
   add_default_sources(sources);
   }

/*************************************************
* Add sources to the list                        *
*************************************************/
void Unix_EntropySource::add_sources(const Unix_Program srcs[], u32bit count)
   {
   sources.insert(sources.end(), srcs, srcs + count);
   std::sort(sources.begin(), sources.end(), Unix_Program_Cmp);
   }

/*************************************************
* Unix Fast Poll                                 *
*************************************************/
u32bit Unix_EntropySource::fast_poll(byte buf[], u32bit length)
   {
   if(length == 0)
      return 0;

   u32bit buf_i = 0;

   const char* STAT_TARGETS[] = {
      "/",
      "/tmp",
      "/var/tmp",
      "/usr",
      "/home",
      "/etc/passwd",
      ".",
      "..",
      0 };

   for(u32bit j = 0; STAT_TARGETS[j]; j++)
      {
      struct stat statbuf;
      clear_mem(&statbuf, 1);
      ::stat(STAT_TARGETS[j], &statbuf);

      buf_i = xor_into_buf(buf, buf_i, length, statbuf);
      }

   u32bit ids[] = {
      ::getpid(),
      ::getppid(),
      ::getuid(),
      ::geteuid(),
      ::getegid(),
      ::getpgrp(),
      ::getsid(0)
   };

   for(u32bit i = 0; i != sizeof(ids); ++i)
      buf_i = xor_into_buf(buf, buf_i, length, ids[i]);

   struct ::rusage usage;

   clear_mem(&usage, 1);
   ::getrusage(RUSAGE_SELF, &usage);
   buf_i = xor_into_buf(buf, buf_i, length, usage);

   ::getrusage(RUSAGE_CHILDREN, &usage);
   buf_i = xor_into_buf(buf, buf_i, length, usage);

   return length;
   }

/*************************************************
* Unix Slow Poll                                 *
*************************************************/
u32bit Unix_EntropySource::slow_poll(byte buf[], u32bit length)
   {
   if(length == 0)
      return 0;

   const u32bit TRY_TO_GET = 16 * 1024;
   const u32bit MINIMAL_WORKING = 32;

   u32bit got = 0;
   u32bit buf_i = 0;

   for(u32bit j = 0; j != sources.size(); j++)
      {
      DataSource_Command pipe(sources[j].name_and_args, PATH);
      SecureVector<byte> buffer(DEFAULT_BUFFERSIZE);

      u32bit got_from_src = 0;

      while(!pipe.end_of_data())
         {
         u32bit this_loop = pipe.read(buffer, buffer.size());
         buf_i = xor_into_buf(buf, buf_i, length, buffer, this_loop);
         got_from_src += this_loop;
         }

      sources[j].working = (got_from_src >= MINIMAL_WORKING) ? true : false;
      got += got_from_src;

      if(got >= TRY_TO_GET)
         break;
      }

   return length;
   }

}
<commit_msg>Fix indexing of ids array. Don't zeroize stat/rusage bufs before use<commit_after>/*************************************************
* Unix EntropySource Source File                 *
* (C) 1999-2008 Jack Lloyd                       *
*************************************************/

#include <botan/es_unix.h>
#include <botan/unix_cmd.h>
#include <botan/parsing.h>
#include <botan/xor_buf.h>
#include <algorithm>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <unistd.h>

namespace Botan {

namespace {

/*************************************************
* Sort ordering by priority                      *
*************************************************/
bool Unix_Program_Cmp(const Unix_Program& a, const Unix_Program& b)
   { return (a.priority < b.priority); }

}

/*************************************************
* Unix_EntropySource Constructor                 *
*************************************************/
Unix_EntropySource::Unix_EntropySource(const std::vector<std::string>& path) :
   PATH(path)
   {
   add_default_sources(sources);
   }

/*************************************************
* Add sources to the list                        *
*************************************************/
void Unix_EntropySource::add_sources(const Unix_Program srcs[], u32bit count)
   {
   sources.insert(sources.end(), srcs, srcs + count);
   std::sort(sources.begin(), sources.end(), Unix_Program_Cmp);
   }

/*************************************************
* Unix Fast Poll                                 *
*************************************************/
u32bit Unix_EntropySource::fast_poll(byte buf[], u32bit length)
   {
   if(length == 0)
      return 0;

   u32bit buf_i = 0;

   const char* stat_targets[] = {
      "/",
      "/tmp",
      "/var/tmp",
      "/usr",
      "/home",
      "/etc/passwd",
      ".",
      "..",
      0 };

   for(u32bit j = 0; stat_targets[j]; j++)
      {
      struct stat statbuf;
      ::stat(stat_targets[j], &statbuf);
      buf_i = xor_into_buf(buf, buf_i, length, statbuf);
      }

   u32bit ids[] = {
      ::getpid(),
      ::getppid(),
      ::getuid(),
      ::geteuid(),
      ::getegid(),
      ::getpgrp(),
      ::getsid(0)
   };

   for(u32bit i = 0; i != sizeof(ids) / sizeof(ids[0]); ++i)
      buf_i = xor_into_buf(buf, buf_i, length, ids[i]);

   struct ::rusage usage;
   ::getrusage(RUSAGE_SELF, &usage);
   buf_i = xor_into_buf(buf, buf_i, length, usage);

   ::getrusage(RUSAGE_CHILDREN, &usage);
   buf_i = xor_into_buf(buf, buf_i, length, usage);

   return length;
   }

/*************************************************
* Unix Slow Poll                                 *
*************************************************/
u32bit Unix_EntropySource::slow_poll(byte buf[], u32bit length)
   {
   if(length == 0)
      return 0;

   const u32bit TRY_TO_GET = 16 * 1024;
   const u32bit MINIMAL_WORKING = 32;

   u32bit got = 0;
   u32bit buf_i = 0;

   for(u32bit j = 0; j != sources.size(); j++)
      {
      DataSource_Command pipe(sources[j].name_and_args, PATH);
      SecureVector<byte> buffer(DEFAULT_BUFFERSIZE);

      u32bit got_from_src = 0;

      while(!pipe.end_of_data())
         {
         u32bit this_loop = pipe.read(buffer, buffer.size());
         buf_i = xor_into_buf(buf, buf_i, length, buffer, this_loop);
         got_from_src += this_loop;
         }

      sources[j].working = (got_from_src >= MINIMAL_WORKING) ? true : false;
      got += got_from_src;

      if(got >= TRY_TO_GET)
         break;
      }

   return length;
   }

}
<|endoftext|>


<commit_before>// __BEGIN_LICENSE__
// Copyright (C) 2006-2011 United States Government as represented by
// the Administrator of the National Aeronautics and Space Administration.
// All Rights Reserved.
// __END_LICENSE__

#include <vw/Plate/PlateFile.h>
#include <vw/Plate/TileManipulation.h>
using namespace vw;
using namespace vw::platefile;

#include <boost/filesystem/convenience.hpp>
#include <boost/program_options.hpp>
#include <boost/foreach.hpp>
namespace po = boost::program_options;
namespace fs = boost::filesystem;

#include <kml/dom.h>
#include <kml/engine/kml_file.h>
#include <kml/base/file.h>

struct Options {
  std::string output_name, url_name, mod_plate_base_url;
};

void handle_arguments(int argc, char* argv[], Options& opt) {
  po::options_description general_options("Extract plate into KML tiles.");
  general_options.add_options()
    ("output_name,o", po::value(&opt.output_name),
     "Output name for the KML result.")
    ("mod_plate", po::value(&opt.mod_plate_base_url),
     "Do not create copy of images. Use mod plate images using user-defined base url.")
    ("help,h", "Display this help message");

  po::options_description hidden_options("");
  hidden_options.add_options()
    ("url", po::value(&opt.url_name), "");

  po::options_description options("");
  options.add(general_options).add(hidden_options);

  po::positional_options_description p;
  p.add("url",-1);

  po::variables_map vm;
  try {
    po::store( po::command_line_parser( argc, argv ).options(options).positional(p).run(), vm );
    po::notify( vm );
  } catch (const po::error& e) {
    vw_throw( ArgumentErr() << "Error parsing input:\n\t"
              << e.what() << options );
  }

  std::ostringstream usage;
  usage << "Usage: " << argv[0] << " <plate url> [options]\n";

  if ( vm.count("help") || opt.url_name.empty() )
    vw_throw( ArgumentErr() << usage.str() << general_options );
  if ( *(opt.url_name.rbegin()) == '/' )
    opt.url_name = opt.url_name.substr(0,opt.url_name.size()-1);
  if ( opt.output_name.empty() ) {
    size_t folder_divide, ext_divide;
    folder_divide = opt.url_name.rfind("/");
    ext_divide = opt.url_name.rfind(".");
    opt.output_name =
      opt.url_name.substr(folder_divide+1,ext_divide-folder_divide-1);
  }
}

kmldom::LatLonAltBoxPtr
create_latlonaltbox( double south, double west, double degree_size,
                     kmldom::KmlFactory* factory ) {
  kmldom::LatLonAltBoxPtr latlonaltbox =
    factory->CreateLatLonAltBox();
  latlonaltbox->set_north( south + degree_size );
  latlonaltbox->set_south( south );
  latlonaltbox->set_east( west + degree_size );
  latlonaltbox->set_west( west );
  return latlonaltbox;
}

kmldom::LatLonBoxPtr
create_latlonbox( double south, double west, double degree_size,
                  kmldom::KmlFactory* factory ) {
  kmldom::LatLonBoxPtr latlonbox =
    factory->CreateLatLonBox();
  latlonbox->set_north( south + degree_size );
  latlonbox->set_south( south );
  latlonbox->set_east( west + degree_size );
  latlonbox->set_west( west );
  return latlonbox;
}

kmldom::LodPtr
create_lod( ssize_t min, ssize_t max, kmldom::KmlFactory* factory ) {
  kmldom::LodPtr lod = factory->CreateLod();
  lod->set_minlodpixels( min );
  lod->set_maxlodpixels( max );
  return lod;
}

std::string prefix_from_location( ssize_t col, ssize_t row, ssize_t level ) {
  std::string result = "";
  size_t count = 0;
  for ( ssize_t i = level; i >= 0; i-- ) {
    if ( (count % 3 == 0) && ( count != 0 ) )
      result += "/";
    size_t mask = 0x01 << i;
    result += ( ( ( col & mask  ) >> i ) |
                ( ( ( row & mask ) >> i ) << 1 ) ) + 48;
    count++;
  }
  return result;
}

class GroundOverlayEngine {
  size_t m_draw_order;

protected:
  kmldom::GroundOverlayPtr
  create_linkless_overlay( TileHeader const& tile,
                           kmldom::KmlFactory* factory,
                           bool lowest_overlay ) const {
    double deg_delta = 360.0 / double( 1 << tile.level() );
    double lon_west = -180.0 + tile.col()*deg_delta;
    double lat_south = 180 - deg_delta*(tile.row()+1);

    kmldom::GroundOverlayPtr goverlay = factory->CreateGroundOverlay();
    kmldom::RegionPtr region = factory->CreateRegion();
    if ( tile.level() == 1 ) {
      // Top layerish. Layer 0 is never drawn because it's
      // boundaries are illegal (Lat range too large). So this step
      // is to make sure layer 1 can always be seen when zoomed out.
      region->set_lod( create_lod( 1, 513, factory ) );
    } else if ( lowest_overlay ) {
      // End of branch
      region->set_lod( create_lod( 128, -1, factory ) );
    } else {
      // Original code went to 1024
      region->set_lod( create_lod( 128, 513, factory ) );
    }
    region->set_latlonaltbox( create_latlonaltbox( lat_south, lon_west,
                                                   deg_delta, factory ) );
    goverlay->set_region( region );
    goverlay->set_latlonbox( create_latlonbox( lat_south, lon_west,
                                               deg_delta, factory ) );
    goverlay->set_draworder( m_draw_order );
    return goverlay;
  }
public:
  GroundOverlayEngine( size_t draw_order = 50 ) : m_draw_order(draw_order){}
};

class LocalEquiEngine : public GroundOverlayEngine {
  boost::shared_ptr<PlateFile> m_platefile;
public:
  LocalEquiEngine( boost::shared_ptr<PlateFile> platefile,
                   size_t draw_order = 50 ) : GroundOverlayEngine(draw_order), m_platefile(platefile) {}

  kmldom::GroundOverlayPtr
  operator()( fs::path kml_location, TileHeader const& tile,
              kmldom::KmlFactory* factory, bool lowest_overlay ) const {

    m_platefile->read_to_file(kml_location.string(), tile.col(), tile.row(),
                              tile.level(), tile.transaction_id(), true);

    kmldom::GroundOverlayPtr goverlay =
      this->create_linkless_overlay( tile, factory, lowest_overlay );
    kmldom::IconPtr icon = factory->CreateIcon();
    icon->set_href(kml_location.replace_extension(m_platefile->default_file_type()).filename());
    goverlay->set_icon( icon );
    return goverlay;
  }
};

class ModPlateEquiEngine : public GroundOverlayEngine {
  std::string m_base_url, m_extension;
public:
  ModPlateEquiEngine( std::string const& base_url,
                      std::string const& extension = "jpg",
                      size_t draw_order = 50 ) :  GroundOverlayEngine(draw_order), m_base_url(base_url), m_extension(extension) {}

  kmldom::GroundOverlayPtr
  operator()( fs::path /*kml_location*/, TileHeader const& tile,
              kmldom::KmlFactory* factory, bool lowest_overlay ) const {
    kmldom::GroundOverlayPtr goverlay =
      this->create_linkless_overlay( tile, factory, lowest_overlay );
    kmldom::IconPtr icon = factory->CreateIcon();
    std::ostringstream ostr;
    ostr << m_base_url << "/" << tile.level() << "/" << tile.row()
         << "/" << tile.col() << "." << m_extension;
    icon->set_href(ostr.str());
    goverlay->set_icon( icon );
    return goverlay;
  }
};

template <class EngineT>
void draw_kml_level ( std::string const& base_folder, ssize_t level,
                      BBox2i const& region, TransactionOrNeg id,
                      boost::shared_ptr<PlateFile> plate,
                      EngineT const& goverlay_engine ) {
  // Do not delete this pointer. LibKML is managing this itself somewhere.
  kmldom::KmlFactory* factory = kmldom::KmlFactory::GetFactory();

  // Searching for transactions
  std::list<TileHeader> tile_records;
  tile_records = plate->search_by_region(level,region,TransactionRange(id));

  // Drawing Level
  BOOST_FOREACH( TileHeader const& t, tile_records ) {
    fs::path path( base_folder + "/" +
                   prefix_from_location( t.col(), t.row(), t.level() ) );
    fs::create_directory( path.parent_path() );

    std::list<TileHeader> links =
      plate->search_by_region(level+1,region*2,TransactionRange(id));

    { // Producing this layer's kml
      kmldom::FolderPtr folder = factory->CreateFolder();
      folder->set_name( path.string() );

      // Create Network Links
      BOOST_FOREACH( TileHeader const& lower, links ) {
        double ldeg_delta = 360.0 / double( 1 << lower.level() );
        double llon_west = -180.0 + lower.col()*ldeg_delta;
        double llat_south = 180 - ldeg_delta*(lower.row()+1);

        kmldom::NetworkLinkPtr netlink = factory->CreateNetworkLink();
        kmldom::RegionPtr region = factory->CreateRegion();
        region->set_lod( create_lod( 128, -1, factory ) );
        region->set_latlonaltbox( create_latlonaltbox( llat_south, llon_west,
                                                       ldeg_delta, factory ) );
        netlink->set_region(region);
        fs::path link_path( base_folder + "/" +
                            prefix_from_location( lower.col(), lower.row(),
                                                  lower.level() ) );
        kmldom::LinkPtr linkptr = factory->CreateLink();
        linkptr->set_viewrefreshmode(kmldom::VIEWREFRESHMODE_ONREGION);
        if ( ( lower.level() % 3 == 0 ) && lower.level() != 0 ) {
          // Pulling out the directory change
          std::string link_string = path.filename()+"/"+link_path.filename()+".kml";
          linkptr->set_href( link_string );
        } else {
          linkptr->set_href( link_path.replace_extension(".kml").filename() );
        }
        netlink->set_link(linkptr);
        folder->add_feature( netlink );
      }

      // Create ground overlay
      folder->add_feature( goverlay_engine( path, t, factory, !links.size() ) );

      // Writing
      kmldom::KmlPtr kml = factory->CreateKml();
      kml->set_feature( folder );
      kmlengine::KmlFilePtr kml_file = kmlengine::KmlFile::CreateFromImport(kml);
      std::string xml_data;
      kml_file->SerializeToString(&xml_data);
      kmlbase::File::WriteStringToFile(xml_data,path.replace_extension(".kml").string());
    } // end kml writing

    // Recursive call
    BOOST_FOREACH( TileHeader const& lower, links ) {
      draw_kml_level( base_folder, lower.level(),
                      BBox2i( lower.col(), lower.row(), 1, 1 ),
                      id, plate, goverlay_engine );
    }
  }
}

int main( int argc, char *argv[] ) {
  Options opt;
  try {
    handle_arguments( argc, argv, opt );

    std::cout << "Opening: " << opt.url_name << "\n";
    boost::shared_ptr<PlateFile> platefile( new PlateFile(opt.url_name) );

    // Create out output directory and chdir
    fs::create_directory( opt.output_name );

    if ( opt.mod_plate_base_url.empty() ) {
      // Standard local platefile
      LocalEquiEngine engine( platefile );
      draw_kml_level( opt.output_name, 0, BBox2i(0,0,1,1),
                      TransactionOrNeg(-1), platefile, engine);
    } else {
      // Recursive call that goes depth first in generation.
      ModPlateEquiEngine engine( opt.mod_plate_base_url );
      draw_kml_level( opt.output_name, 0, BBox2i(0,0,1,1),
                      TransactionOrNeg(-1), platefile, engine);
    }

    // Rename 0.kml to be the opening kml
    fs::rename( fs::path(opt.output_name) / "0.kml",
                fs::path(opt.output_name) / (opt.output_name+".kml") );

  } catch ( const ArgumentErr& e ) {
    VW_OUT() << e.what() << std::endl;
    return 1;
  } catch ( const Exception& e ) {
    std::cerr << "Error: " << e.what() << std::endl;
    return 1;
  }

  return 0;
}
<commit_msg>plate2kml: This was renamed to plate2cloud, undo mistaken checkin<commit_after><|endoftext|>


<commit_before>// Copyright (c) 2018 The Gulden developers
// Authored by: Willem de Jonge (willem@isnapp.nl)
// Distributed under the GULDEN software license, see the accompanying
// file COPYING

#include "spvscanner.h"
#include "../net_processing.h"
#include "timedata.h"
#include "../validation/validation.h"
#include "wallet.h"

#include <algorithm>

// Maximum number of requests pending. This number should be high enough so that
// the requests can be reasonably distributed over our peers.
const int MAX_PENDING_REQUESTS = 512;

// Duration (seconds) of longest fork that can be handled
const int64_t MAX_FORK_DURATION = 1 * 12 * 3600;

// Persisting wallet db updates are limited as they are very expensive
// and updating at every processed block would give very poor performance.
// Therefore the state is only written to the db when a minimum time interval has passed,
// or a certain of number of blocks have been processed (whichever comes first).
// Note that due to this some blocks might be re-processed in case of abnormal termination,
// this is fine.

// Interval in seconds for writing the scan progress to the wallet db
const int64_t PERSIST_INTERVAL_SEC = 5;

// Blocks count after which scan processing state is written to the db.
const int PERSIST_BLOCK_COUNT = 500;

// limit UI update notifications (except when catched up)
const int UI_UPDATE_LIMIT = 50;

std::atomic<int> CSPVScanner::lastProcessedHeight = 0;

CSPVScanner::CSPVScanner(CWallet& _wallet) :
    wallet(_wallet),
    numConnections(0)
{
    LOCK(cs_main);
    Init();
}

CSPVScanner::~CSPVScanner()
{
    uiInterface.NotifyNumConnectionsChanged.disconnect(boost::bind(&CSPVScanner::OnNumConnectionsChanged, this, _1));
}

void CSPVScanner::Init()
{
    AssertLockHeld(cs_main);

    lastProcessed = nullptr;
    lastProgressReported = -1.0f;
    lastPersistedBlockTime = 0;
    lastPersistTime = 0;
    startHeight = -1;

    // init scan starting time to birth of first key
    startTime =  wallet.nTimeFirstKey;

    // forward scan starting time to last block processed if available
    CWalletDB walletdb(*wallet.dbw);
    CBlockLocator locator;
    if (walletdb.ReadLastSPVBlockProcessed(locator, lastPersistedBlockTime))
        startTime = lastPersistedBlockTime;

    // rewind scan starting time by maximum handled fork duration
    startTime = std::max(Params().GenesisBlock().GetBlockTime(), startTime - MAX_FORK_DURATION);
}

void CSPVScanner::ResetScan()
{
    LOCK(cs_main);

    // erase persisted spv progress
    CWalletDB walletdb(*wallet.dbw);
    walletdb.EraseLastSPVBlockProcessed();

    Init();

    ResetUnifiedProgressNotification();
}

bool CSPVScanner::StartScan()
{
    LOCK(cs_main);
    if (StartPartialHeaders(startTime, std::bind(&CSPVScanner::HeaderTipChanged, this, std::placeholders::_1)))
    {
        uiInterface.NotifyNumConnectionsChanged.connect(boost::bind(&CSPVScanner::OnNumConnectionsChanged, this, _1));
        HeaderTipChanged(partialChain.Tip());
        NotifyUnifiedProgress();
        return true;
    }
    else
        return false;
}

const CBlockIndex* CSPVScanner::LastBlockProcessed() const
{
    LOCK(cs_main);
    return lastProcessed;
}

void CSPVScanner::RequestBlocks()
{
    LOCK2(cs_main, wallet.cs_wallet);

    // put lastProcessed and/or requestTip back on chain if forked
    while (!partialChain.Contains(requestTip)) {
        if (requestTip->nHeight > lastProcessed->nHeight) {
            CancelPriorityDownload(requestTip, std::bind(&CSPVScanner::ProcessPriorityRequest, this, std::placeholders::_1, std::placeholders::_2));
            requestTip = requestTip->pprev;
        }
        else { // so here requestTip == lastProcessed
            std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
            if (ReadBlockFromDisk(*pblock, lastProcessed, Params())) {
                wallet.BlockDisconnected(pblock);
            }
            else {
                // This block must be on disk, it was processed before.
                // So pruning has to keep at least as many blocks back as the longest fork we are willing to handle.
                assert(false);
            }
            UpdateLastProcessed(lastProcessed->pprev);
            requestTip = lastProcessed;
        }
    }

    // skip blocks that are before startTime
    const CBlockIndex* skip = lastProcessed;
    while (skip->GetBlockTime() < startTime && partialChain.Height() > skip->nHeight)
        skip = partialChain.Next(skip);
    if (skip != lastProcessed) {
        LogPrint(BCLog::WALLET, "Skipping %d old blocks for SPV scan, up to height %d\n", skip->nHeight - lastProcessed->nHeight, skip->nHeight);
        UpdateLastProcessed(skip);
        if (lastProcessed->nHeight > requestTip->nHeight) {
            requestTip = lastProcessed;
        }
    }

    std::vector<const CBlockIndex*> blocksToRequest;

    // add requests for as long as nMaxPendingRequests is not reached and there are still heigher blocks in headerChain
    while (requestTip->nHeight - lastProcessed->nHeight < MAX_PENDING_REQUESTS &&
           partialChain.Height() > requestTip->nHeight) {

        requestTip = partialChain.Next(requestTip);
        blocksToRequest.push_back(requestTip);
    }

    if (!blocksToRequest.empty()) {
        LogPrint(BCLog::WALLET, "Requesting %d blocks for SPV, up to height %d\n", blocksToRequest.size(), requestTip->nHeight);
        AddPriorityDownload(blocksToRequest, std::bind(&CSPVScanner::ProcessPriorityRequest, this, std::placeholders::_1, std::placeholders::_2));
    }
}

void CSPVScanner::ProcessPriorityRequest(const std::shared_ptr<const CBlock> &block, const CBlockIndex *pindex)
{
    LOCK2(cs_main, wallet.cs_wallet);

    // if chainActive is up-to-date no SPV blocks need to be requested, we can update SPV to the activeChain
    if (chainActive.Tip() == partialChain.Tip()) {
        LogPrint(BCLog::WALLET, "chainActive is up-to-date, skipping SPV processing block %d\n", pindex->nHeight);
        if (lastProcessed != partialChain.Tip()) {
            UpdateLastProcessed(chainActive.Tip());
            requestTip = lastProcessed;
        }
    }

    // if this is the block we're waiting for process it and request new block(s)
    if (pindex->pprev == lastProcessed) {
        LogPrint(BCLog::WALLET, "SPV processing block %d\n", pindex->nHeight);

        std::vector<CTransactionRef> vtxConflicted; // dummy for now
        wallet.BlockConnected(block, pindex, vtxConflicted);

        UpdateLastProcessed(pindex);

        RequestBlocks();

        if (partialChain.Height() == pindex->nHeight || pindex->nHeight % UI_UPDATE_LIMIT == 0)
            uiInterface.NotifySPVProgress(startHeight, pindex->nHeight, partialChain.Height());

        NotifyUnifiedProgress();

        blocksSincePersist++;

        ExpireMempoolForPartialSync(lastProcessed);
    }
}

void CSPVScanner::HeaderTipChanged(const CBlockIndex* pTip)
{
    LOCK(cs_main);
    if (pTip)
    {
        // initialization on the first header tip notification
        if (lastProcessed == nullptr)
        {
            if (partialChain.Height() >= partialChain.HeightOffset()
                    && partialChain[partialChain.HeightOffset()]->GetBlockTime() <= startTime)
            {
                // use start of partial chain to init lastProcessed
                // forks are handled when requesting blocks which will also fast-forward to startTime
                // should the headerChain be very early
                lastProcessed = partialChain[partialChain.HeightOffset()];
                requestTip = lastProcessed;
                startHeight = lastProcessed->nHeight;

                LogPrint(BCLog::WALLET, "SPV init using %s (height = %d) as last processed block\n",
                         lastProcessed->GetBlockHashPoW2().ToString(), lastProcessed->nHeight);
            }
            else
            {
                // headerChain not usable, it does not start early enough or has no data.
                // This should not happen, as StartPartialHeaders was explicitly given the startTime
                // so if this occurs it's a bug.
                throw std::runtime_error("partialChain not usable, starting late or too little data");
            }
        }

        RequestBlocks();

        NotifyUnifiedProgress();
    }
    else // pTip == nullptr => partial sync stopped
    {
        CancelAllPriorityDownloads();
        Persist();
    }
}

void CSPVScanner::OnNumConnectionsChanged(int newNumConnections)
{
    LOCK(cs_main);

    numConnections = newNumConnections;
    NotifyUnifiedProgress();
}

void CSPVScanner::ResetUnifiedProgressNotification()
{
    LOCK(cs_main);

    lastProgressReported = -1.0f;
    startHeight = -1;
    if (lastProcessed)
        startHeight = lastProcessed->nHeight;
    NotifyUnifiedProgress();
}

void CSPVScanner::NotifyUnifiedProgress()
{
    AssertLockHeld(cs_main);

    const float CONNECTION_WEIGHT = 0.05f;
    const float MIN_REPORTING_DELTA = 0.002f;
    const float ALWAYS_REPORT_THRESHOLD = 0.9995f;

    float newProgress = 0.0f;

    // Only calculate progress if there are connections. Without connections progress is reported as zero
    // which is the only case where progress can decrease during a session (ie. if all connections are lost)
    if (numConnections > 0) {
        newProgress += CONNECTION_WEIGHT;

        int probableHeight = GetProbableHeight();

        if (probableHeight > 0 && startHeight >= 0 &&
            probableHeight != startHeight &&
            lastProcessed != nullptr && lastProcessed->nHeight > 0)
        {
            float pgs = (lastProcessed->nHeight - startHeight)/float(probableHeight - startHeight);
            newProgress += (1.0f - CONNECTION_WEIGHT) * pgs;
        }
        else if (probableHeight == startHeight)
            newProgress = 1.0f;

        // silently ignore progress decrease, this can occur if the chain grew
        // faster then the synchronisation (this would be a very short lived situation)
        // and reporting will continue normally when catching up
        // Note that newProgress is always >= 0 here. So when progress reporting is reset (lastProgressReported = -1)
        // it is not handled as a decrease.
        if (newProgress <= lastProgressReported && lastProgressReported != 1.0f)
            return;

        // Limit processing overhead and only report if a reasonable amount of progress was made since last report.
        // But don't limit if progress reporting was reset (lastProgressReported = -1)
        // and also don't limit if we are near the end (ALWAYS_REPORT_THRESHOLD).
        if (lastProgressReported >= 0.0f  && lastProgressReported != 1.0f && newProgress - lastProgressReported <= MIN_REPORTING_DELTA && newProgress < ALWAYS_REPORT_THRESHOLD)
            return;
    }

    if (newProgress != lastProgressReported) {
        uiInterface.NotifyUnifiedProgress(newProgress);
        lastProgressReported = newProgress;
    }
}

void CSPVScanner::UpdateLastProcessed(const CBlockIndex* pindex)
{
    AssertLockHeld(cs_main);

    lastProcessed = pindex;

    lastProcessedHeight = lastProcessed ? lastProcessed->nHeight : 0;

    int64_t now = GetAdjustedTime();
    if (now - lastPersistTime > PERSIST_INTERVAL_SEC || blocksSincePersist >= PERSIST_BLOCK_COUNT)
        Persist();
}

void CSPVScanner::Persist()
{
    LOCK(cs_main);

    if (lastProcessed != nullptr && lastProcessed->GetBlockTime() > lastPersistedBlockTime)
    {
        // persist & prune block index
        PersistAndPruneForPartialSync();

        // persist lastProcessed
        CWalletDB walletdb(*wallet.dbw);
        walletdb.WriteLastSPVBlockProcessed(partialChain.GetLocatorPoW2(lastProcessed), lastProcessed->GetBlockTime());

        // now that we are sure both the index and lastProcessed time locator have been saved
        // compute the new pruning height for the next iteration

        int64_t forkTimeLimit = lastProcessed->GetBlockTime() - 2 * MAX_FORK_DURATION;

        int maxPruneHeight =
                // determine oldest block that is at most forkTimeLimit in the past
                partialChain.LowerBound(partialChain.HeightOffset(),
                                        std::min(lastProcessed->nHeight, partialChain.Height()),
                                        forkTimeLimit,
                                        [](const CBlockIndex* index, int64_t limit){ return index->GetBlockTime() < limit; })
                // the block before that is the youngest that is more than forkTimeLimit ago
                - 1
                // the window required to do context checks on the headers
                - 576;

        if (maxPruneHeight > 0)
            SetMaxSPVPruneHeight(maxPruneHeight);

        lastPersistTime = GetAdjustedTime();
        lastPersistedBlockTime = lastProcessed->GetBlockTime();
        blocksSincePersist = 0;
    }
}

int CSPVScanner::getProcessedHeight()
{
    return lastProcessedHeight;
}
<commit_msg>Unity prevent progress indication "flashing" and simplify progress notification rules<commit_after>// Copyright (c) 2018 The Gulden developers
// Authored by: Willem de Jonge (willem@isnapp.nl)
// Distributed under the GULDEN software license, see the accompanying
// file COPYING

#include "spvscanner.h"
#include "../net_processing.h"
#include "timedata.h"
#include "../validation/validation.h"
#include "wallet.h"

#include <algorithm>

// Maximum number of requests pending. This number should be high enough so that
// the requests can be reasonably distributed over our peers.
const int MAX_PENDING_REQUESTS = 512;

// Duration (seconds) of longest fork that can be handled
const int64_t MAX_FORK_DURATION = 1 * 12 * 3600;

// Persisting wallet db updates are limited as they are very expensive
// and updating at every processed block would give very poor performance.
// Therefore the state is only written to the db when a minimum time interval has passed,
// or a certain of number of blocks have been processed (whichever comes first).
// Note that due to this some blocks might be re-processed in case of abnormal termination,
// this is fine.

// Interval in seconds for writing the scan progress to the wallet db
const int64_t PERSIST_INTERVAL_SEC = 5;

// Blocks count after which scan processing state is written to the db.
const int PERSIST_BLOCK_COUNT = 500;

// limit UI update notifications (except when catched up)
const int UI_UPDATE_LIMIT = 50;

std::atomic<int> CSPVScanner::lastProcessedHeight = 0;

CSPVScanner::CSPVScanner(CWallet& _wallet) :
    wallet(_wallet),
    numConnections(0)
{
    LOCK(cs_main);
    Init();
}

CSPVScanner::~CSPVScanner()
{
    uiInterface.NotifyNumConnectionsChanged.disconnect(boost::bind(&CSPVScanner::OnNumConnectionsChanged, this, _1));
}

void CSPVScanner::Init()
{
    AssertLockHeld(cs_main);

    lastProcessed = nullptr;
    lastProgressReported = -1.0f;
    lastPersistedBlockTime = 0;
    lastPersistTime = 0;
    startHeight = -1;

    // init scan starting time to birth of first key
    startTime =  wallet.nTimeFirstKey;

    // forward scan starting time to last block processed if available
    CWalletDB walletdb(*wallet.dbw);
    CBlockLocator locator;
    if (walletdb.ReadLastSPVBlockProcessed(locator, lastPersistedBlockTime))
        startTime = lastPersistedBlockTime;

    // rewind scan starting time by maximum handled fork duration
    startTime = std::max(Params().GenesisBlock().GetBlockTime(), startTime - MAX_FORK_DURATION);
}

void CSPVScanner::ResetScan()
{
    LOCK(cs_main);

    // erase persisted spv progress
    CWalletDB walletdb(*wallet.dbw);
    walletdb.EraseLastSPVBlockProcessed();

    Init();

    ResetUnifiedProgressNotification();
}

bool CSPVScanner::StartScan()
{
    LOCK(cs_main);
    if (StartPartialHeaders(startTime, std::bind(&CSPVScanner::HeaderTipChanged, this, std::placeholders::_1)))
    {
        uiInterface.NotifyNumConnectionsChanged.connect(boost::bind(&CSPVScanner::OnNumConnectionsChanged, this, _1));
        HeaderTipChanged(partialChain.Tip());
        NotifyUnifiedProgress();
        return true;
    }
    else
        return false;
}

const CBlockIndex* CSPVScanner::LastBlockProcessed() const
{
    LOCK(cs_main);
    return lastProcessed;
}

void CSPVScanner::RequestBlocks()
{
    LOCK2(cs_main, wallet.cs_wallet);

    // put lastProcessed and/or requestTip back on chain if forked
    while (!partialChain.Contains(requestTip)) {
        if (requestTip->nHeight > lastProcessed->nHeight) {
            CancelPriorityDownload(requestTip, std::bind(&CSPVScanner::ProcessPriorityRequest, this, std::placeholders::_1, std::placeholders::_2));
            requestTip = requestTip->pprev;
        }
        else { // so here requestTip == lastProcessed
            std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
            if (ReadBlockFromDisk(*pblock, lastProcessed, Params())) {
                wallet.BlockDisconnected(pblock);
            }
            else {
                // This block must be on disk, it was processed before.
                // So pruning has to keep at least as many blocks back as the longest fork we are willing to handle.
                assert(false);
            }
            UpdateLastProcessed(lastProcessed->pprev);
            requestTip = lastProcessed;
        }
    }

    // skip blocks that are before startTime
    const CBlockIndex* skip = lastProcessed;
    while (skip->GetBlockTime() < startTime && partialChain.Height() > skip->nHeight)
        skip = partialChain.Next(skip);
    if (skip != lastProcessed) {
        LogPrint(BCLog::WALLET, "Skipping %d old blocks for SPV scan, up to height %d\n", skip->nHeight - lastProcessed->nHeight, skip->nHeight);
        UpdateLastProcessed(skip);
        if (lastProcessed->nHeight > requestTip->nHeight) {
            requestTip = lastProcessed;
        }
    }

    std::vector<const CBlockIndex*> blocksToRequest;

    // add requests for as long as nMaxPendingRequests is not reached and there are still heigher blocks in headerChain
    while (requestTip->nHeight - lastProcessed->nHeight < MAX_PENDING_REQUESTS &&
           partialChain.Height() > requestTip->nHeight) {

        requestTip = partialChain.Next(requestTip);
        blocksToRequest.push_back(requestTip);
    }

    if (!blocksToRequest.empty()) {
        LogPrint(BCLog::WALLET, "Requesting %d blocks for SPV, up to height %d\n", blocksToRequest.size(), requestTip->nHeight);
        AddPriorityDownload(blocksToRequest, std::bind(&CSPVScanner::ProcessPriorityRequest, this, std::placeholders::_1, std::placeholders::_2));
    }
}

void CSPVScanner::ProcessPriorityRequest(const std::shared_ptr<const CBlock> &block, const CBlockIndex *pindex)
{
    LOCK2(cs_main, wallet.cs_wallet);

    // if chainActive is up-to-date no SPV blocks need to be requested, we can update SPV to the activeChain
    if (chainActive.Tip() == partialChain.Tip()) {
        LogPrint(BCLog::WALLET, "chainActive is up-to-date, skipping SPV processing block %d\n", pindex->nHeight);
        if (lastProcessed != partialChain.Tip()) {
            UpdateLastProcessed(chainActive.Tip());
            requestTip = lastProcessed;
        }
    }

    // if this is the block we're waiting for process it and request new block(s)
    if (pindex->pprev == lastProcessed) {
        LogPrint(BCLog::WALLET, "SPV processing block %d\n", pindex->nHeight);

        std::vector<CTransactionRef> vtxConflicted; // dummy for now
        wallet.BlockConnected(block, pindex, vtxConflicted);

        UpdateLastProcessed(pindex);

        RequestBlocks();

        if (partialChain.Height() == pindex->nHeight || pindex->nHeight % UI_UPDATE_LIMIT == 0)
            uiInterface.NotifySPVProgress(startHeight, pindex->nHeight, partialChain.Height());

        NotifyUnifiedProgress();

        blocksSincePersist++;

        ExpireMempoolForPartialSync(lastProcessed);
    }
}

void CSPVScanner::HeaderTipChanged(const CBlockIndex* pTip)
{
    LOCK(cs_main);
    if (pTip)
    {
        // initialization on the first header tip notification
        if (lastProcessed == nullptr)
        {
            if (partialChain.Height() >= partialChain.HeightOffset()
                    && partialChain[partialChain.HeightOffset()]->GetBlockTime() <= startTime)
            {
                // use start of partial chain to init lastProcessed
                // forks are handled when requesting blocks which will also fast-forward to startTime
                // should the headerChain be very early
                lastProcessed = partialChain[partialChain.HeightOffset()];
                requestTip = lastProcessed;
                startHeight = lastProcessed->nHeight;

                LogPrint(BCLog::WALLET, "SPV init using %s (height = %d) as last processed block\n",
                         lastProcessed->GetBlockHashPoW2().ToString(), lastProcessed->nHeight);
            }
            else
            {
                // headerChain not usable, it does not start early enough or has no data.
                // This should not happen, as StartPartialHeaders was explicitly given the startTime
                // so if this occurs it's a bug.
                throw std::runtime_error("partialChain not usable, starting late or too little data");
            }
        }

        RequestBlocks();

        NotifyUnifiedProgress();
    }
    else // pTip == nullptr => partial sync stopped
    {
        CancelAllPriorityDownloads();
        Persist();
    }
}

void CSPVScanner::OnNumConnectionsChanged(int newNumConnections)
{
    LOCK(cs_main);

    numConnections = newNumConnections;
    NotifyUnifiedProgress();
}

void CSPVScanner::ResetUnifiedProgressNotification()
{
    LOCK(cs_main);

    lastProgressReported = -1.0f;
    startHeight = -1;
    if (lastProcessed)
        startHeight = lastProcessed->nHeight;
    NotifyUnifiedProgress();
}

void CSPVScanner::NotifyUnifiedProgress()
{
    AssertLockHeld(cs_main);

    const float CONNECTION_WEIGHT = 0.05f;
    const float MIN_REPORTING_DELTA = 0.005f;

    float newProgress = 0.0f;

    // Only calculate progress if there are connections. Without connections progress is reported as zero
    // which is the only case where progress can decrease during a session (ie. if all connections are lost)
    if (numConnections > 0) {
        newProgress += CONNECTION_WEIGHT;

        int probableHeight = GetProbableHeight();

        if (probableHeight > 0 && startHeight >= 0 &&
            probableHeight != startHeight &&
            lastProcessed != nullptr && lastProcessed->nHeight > 0)
        {
            float pgs = (lastProcessed->nHeight - startHeight)/float(probableHeight - startHeight);
            newProgress += (1.0f - CONNECTION_WEIGHT) * pgs;
        }
        else if (probableHeight == startHeight)
            newProgress = 1.0f;

        // Limit progress notification to reduce overhead, only notify if delta since previous ntf is > MIN_REPORTING_DELTA
        if (lastProgressReported >= 0.0f && newProgress < 1.0f && fabs(newProgress - lastProgressReported) < MIN_REPORTING_DELTA)
                return;
        // else lastProgressReported < 0 => progress was reset/initialized so always notify
    }

    if (newProgress != lastProgressReported) {
        uiInterface.NotifyUnifiedProgress(newProgress);
        lastProgressReported = newProgress;
    }
}

void CSPVScanner::UpdateLastProcessed(const CBlockIndex* pindex)
{
    AssertLockHeld(cs_main);

    lastProcessed = pindex;

    lastProcessedHeight = lastProcessed ? lastProcessed->nHeight : 0;

    int64_t now = GetAdjustedTime();
    if (now - lastPersistTime > PERSIST_INTERVAL_SEC || blocksSincePersist >= PERSIST_BLOCK_COUNT)
        Persist();
}

void CSPVScanner::Persist()
{
    LOCK(cs_main);

    if (lastProcessed != nullptr && lastProcessed->GetBlockTime() > lastPersistedBlockTime)
    {
        // persist & prune block index
        PersistAndPruneForPartialSync();

        // persist lastProcessed
        CWalletDB walletdb(*wallet.dbw);
        walletdb.WriteLastSPVBlockProcessed(partialChain.GetLocatorPoW2(lastProcessed), lastProcessed->GetBlockTime());

        // now that we are sure both the index and lastProcessed time locator have been saved
        // compute the new pruning height for the next iteration

        int64_t forkTimeLimit = lastProcessed->GetBlockTime() - 2 * MAX_FORK_DURATION;

        int maxPruneHeight =
                // determine oldest block that is at most forkTimeLimit in the past
                partialChain.LowerBound(partialChain.HeightOffset(),
                                        std::min(lastProcessed->nHeight, partialChain.Height()),
                                        forkTimeLimit,
                                        [](const CBlockIndex* index, int64_t limit){ return index->GetBlockTime() < limit; })
                // the block before that is the youngest that is more than forkTimeLimit ago
                - 1
                // the window required to do context checks on the headers
                - 576;

        if (maxPruneHeight > 0)
            SetMaxSPVPruneHeight(maxPruneHeight);

        lastPersistTime = GetAdjustedTime();
        lastPersistedBlockTime = lastProcessed->GetBlockTime();
        blocksSincePersist = 0;
    }
}

int CSPVScanner::getProcessedHeight()
{
    return lastProcessedHeight;
}
<|endoftext|>