refs #921, distribution value object/object list

* registration
* property index
This commit is contained in:
Klaus Basan
2017-03-27 23:11:14 +02:00
committed by Mathew Sutcliffe
parent e787d50e47
commit 5ce704c963
11 changed files with 572 additions and 2 deletions

View File

@@ -16,6 +16,7 @@
#include "blackmisc/aviation/aircrafticaocodelist.h"
#include "blackmisc/aviation/airlineicaocodelist.h"
#include "blackmisc/db/dbinfolist.h"
#include "blackmisc/db/distributionlist.h"
#include "blackmisc/simulation/aircraftmodellist.h"
#include "blackmisc/simulation/distributorlist.h"
@@ -170,6 +171,7 @@ namespace BlackMisc
template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IDatastoreObjectList<BlackMisc::Aviation::CAircraftIcaoCode, BlackMisc::Aviation::CAircraftIcaoCodeList, int>;
template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IDatastoreObjectList<BlackMisc::Aviation::CAirlineIcaoCode, BlackMisc::Aviation::CAirlineIcaoCodeList, int>;
template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IDatastoreObjectList<BlackMisc::Db::CDbInfo, BlackMisc::Db::CDbInfoList, int>;
template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IDatastoreObjectList<BlackMisc::Db::CDistribution, BlackMisc::Db::CDistributionList, int>;
template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IDatastoreObjectList<BlackMisc::Simulation::CAircraftModel, BlackMisc::Simulation::CAircraftModelList, int>;
template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IDatastoreObjectList<BlackMisc::Simulation::CDistributor, BlackMisc::Simulation::CDistributorList, QString>;
template class BLACKMISC_EXPORT_DEFINE_TEMPLATE IDatastoreObjectList<BlackMisc::CCountry, BlackMisc::CCountryList, QString>;

View File

@@ -13,7 +13,6 @@
#define BLACKMISC_DB_DATABASEOBJECTLIST_H
#include "blackmisc/timestampobjectlist.h"
#include <QJsonArray>
#include <QSet>
#include <QString>
@@ -70,6 +69,7 @@ namespace BlackMisc
extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IDatastoreObjectList<BlackMisc::Aviation::CAircraftIcaoCode, BlackMisc::Aviation::CAircraftIcaoCodeList, int>;
extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IDatastoreObjectList<BlackMisc::Aviation::CAirlineIcaoCode, BlackMisc::Aviation::CAirlineIcaoCodeList, int>;
extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IDatastoreObjectList<BlackMisc::Db::CDbInfo, BlackMisc::Db::CDbInfoList, int>;
extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IDatastoreObjectList<BlackMisc::Db::CDistribution, BlackMisc::Db::CDistributionList, int>;
extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IDatastoreObjectList<BlackMisc::Simulation::CAircraftModel, BlackMisc::Simulation::CAircraftModelList, int>;
extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IDatastoreObjectList<BlackMisc::Simulation::CDistributor, BlackMisc::Simulation::CDistributorList, QString>;
extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE IDatastoreObjectList<BlackMisc::CCountry, BlackMisc::CCountryList, QString>;

View File

@@ -21,5 +21,7 @@
#include "blackmisc/db/dbinfolist.h"
#include "blackmisc/db/dbinfo.h"
#include "blackmisc/db/dbflags.h"
#include "blackmisc/db/distribution.h"
#include "blackmisc/db/distributionlist.h"
#endif // guard

View File

@@ -0,0 +1,283 @@
/* Copyright (C) 2017
* swift project community / contributors
*
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
* directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project,
* including this file, may be copied, modified, propagated, or distributed except according to the terms
* contained in the LICENSE file.
*/
#include "distribution.h"
#include "blackconfig/buildconfig.h"
#include <QStringBuilder>
using namespace BlackConfig;
using namespace BlackMisc;
using namespace BlackMisc::Network;
namespace BlackMisc
{
namespace Db
{
CDistribution::CDistribution()
{ }
CDistribution::CDistribution(const QString &channel, bool restricted) :
m_channel(channel.trimmed().toUpper()), m_restricted(restricted)
{ }
void CDistribution::setChannel(const QString &channel)
{
m_channel = channel.trimmed().toUpper();
}
QStringList CDistribution::getPlatforms() const
{
QStringList platforms = m_platformFiles.keys();
platforms.sort();
return platforms;
}
QString CDistribution::guessPlatform() const
{
const QStringList platforms(getPlatforms());
if (platforms.isEmpty()) { return ""; }
QStringList reduced;
for (const QString &p : platforms)
{
if (CBuildConfig::isRunningOnWindowsNtPlatform())
{
if (!p.contains("win", Qt::CaseInsensitive)) continue;
}
else if (CBuildConfig::isRunningOnLinuxPlatform())
{
if (!p.contains("linux", Qt::CaseInsensitive)) continue;
}
else if (CBuildConfig::isRunningOnMacOSXPlatform())
{
if (!p.contains("mac", Qt::CaseInsensitive) || !p.contains("osx", Qt::CaseInsensitive)) continue;
}
reduced << p;
}
if (reduced.size() > 1)
{
// further reduce by VATSIM flag
QStringList furtherReduced;
for (const QString &p : reduced)
{
if (CBuildConfig::isVatsimVersion())
{
if (p.contains("vatsim")) { furtherReduced << p; }
}
else
{
if (!p.contains("vatsim")) { furtherReduced << p; }
}
}
if (!furtherReduced.isEmpty()) { reduced = furtherReduced; }
}
if (reduced.isEmpty()) { return ""; }
return reduced.front();
}
QString CDistribution::getVersionString(const QString &platform) const
{
if (platform.isEmpty()) { return ""; }
return m_platformVersions.value(platform);
}
QVersionNumber CDistribution::getQVersion(const QString &platform) const
{
return QVersionNumber::fromString(getVersionString(platform));
}
QString CDistribution::getFilename(const QString &platform) const
{
if (platform.isEmpty()) { return ""; }
return m_platformFiles.value(platform);
}
void CDistribution::addDownloadUrl(const CUrl &url)
{
if (url.isEmpty()) { return; }
this->m_downloadUrls.push_back(url);
}
bool CDistribution::hasDownloadUrls() const
{
return !this->m_downloadUrls.isEmpty();
}
QString CDistribution::convertToQString(bool i18n) const
{
return convertToQString(", ", i18n);
}
QString CDistribution::convertToQString(const QString &separator, bool i18n) const
{
return QLatin1String("channel: ") %
this->getChannel() %
separator %
QLatin1String("download URLs: ") %
getDownloadUrls().toQString(i18n) %
separator %
QLatin1String("platforms: ") %
getPlatforms().join(", ") %
separator %
QLatin1String("timestamp: ") %
this->getFormattedUtcTimestampYmdhms();
}
CVariant CDistribution::propertyByIndex(const BlackMisc::CPropertyIndex &index) const
{
if (index.isMyself()) { return CVariant::from(*this); }
if (IDatastoreObjectWithIntegerKey::canHandleIndex(index)) { return IDatastoreObjectWithIntegerKey::propertyByIndex(index); }
const ColumnIndex i = index.frontCasted<ColumnIndex>();
switch (i)
{
case IndexChannel:
return CVariant::fromValue(this->m_channel);
case IndexDownloadUrls:
return CVariant::fromValue(this->m_downloadUrls);
case IndexRestricted:
return CVariant::fromValue(this->m_restricted);
case IndexPlatforms:
return CVariant::fromValue(this->getPlatforms());
case IndexPlatformFiles:
return CVariant::fromValue(this->m_platformFiles);
default:
return CValueObject::propertyByIndex(index);
}
}
void CDistribution::setPropertyByIndex(const CPropertyIndex &index, const CVariant &variant)
{
if (index.isMyself()) { (*this) = variant.to<CDistribution>(); return; }
if (IDatastoreObjectWithIntegerKey::canHandleIndex(index))
{
IDatastoreObjectWithIntegerKey::setPropertyByIndex(index, variant);
return;
}
const ColumnIndex i = index.frontCasted<ColumnIndex>();
switch (i)
{
case IndexChannel:
this->setChannel(variant.value<QString>());
break;
case IndexDownloadUrls:
this->m_downloadUrls = variant.value<CUrlList>();
break;
case IndexRestricted:
this->m_restricted = variant.toBool();
break;
case IndexPlatformFiles:
this->m_platformFiles = variant.value<CPlatformDictionary>();
break;
default:
CValueObject::setPropertyByIndex(index, variant);
break;
}
}
CDistribution CDistribution::fromDatabaseJson(const QJsonObject &json, const QString &prefix)
{
Q_UNUSED(prefix); // not nested
const QString channel(json.value("channel").toString());
const bool restricted(json.value("restricted").toBool());
CDistribution distribution(channel, restricted);
// add the URLs
for (int i = 0; i < 5; i++)
{
const QString key = "url" + QString::number(i);
const QString url = json.value(key).toString();
if (url.isEmpty()) { continue; }
distribution.addDownloadUrl(CUrl(url));
}
const QJsonObject platforms = json.value("platforms").toObject();
const QStringList platformsKeys = platforms.keys();
if (platformsKeys.isEmpty()) { return CDistribution(); } // no platforms, then the whole distribution is useless
for (const QString platformKey : platformsKeys)
{
QStringList platformFileNames;
QJsonArray platformFiles = platforms.value(platformKey).toArray();
for (const QJsonValue &platformFile : platformFiles)
{
const QString filename = platformFile.toString();
if (filename.isEmpty()) { continue; }
platformFileNames << filename;
}
const QPair<QString, QVersionNumber> latestFileInfo = findLatestVersion(platformFileNames);
if (!latestFileInfo.first.isEmpty())
{
distribution.m_platformFiles.insert(platformKey, latestFileInfo.first);
distribution.m_platformVersions.insert(platformKey, latestFileInfo.second.toString());
}
}
distribution.setKeyAndTimestampFromDatabaseJson(json);
return distribution;
}
QVersionNumber CDistribution::versionNumberFromFilename(const QString &filename)
{
if (filename.isEmpty()) { return QVersionNumber(); }
// swift-installer-linux-64-0.7.3_2017-03-25_11-24-50.run
static const QRegExp firstSegments("\\d+\\.\\d+\\.\\d+");
if (firstSegments.indexIn(filename) < 0)
{
return QVersionNumber(); // no version, invalid
}
QString v = firstSegments.cap(0); // first 3 segments, like 0.9.3
if (!v.endsWith('.')) { v += '.'; }
static const QRegExp ts1("\\d{4}.\\d{2}.\\d{2}.\\d{2}.\\d{2}.\\d{2}");
if (ts1.indexIn(filename) < 0)
{
// version without timestamp
v += "0";
return QVersionNumber::fromString(v);
}
const QString versionTimestampString = BlackMisc::digitOnlyString(ts1.cap(0));
const QDateTime versionTimestamp = QDateTime::fromString(versionTimestampString, "yyyyMMddHHmmss");
const QString lastSegment = QString::number(CBuildConfig::buildTimestampAsVersionSegment(versionTimestamp));
v += lastSegment;
return QVersionNumber::fromString(v);
}
QPair<QString, QVersionNumber> CDistribution::findLatestVersion(const QStringList &filenames)
{
if (filenames.isEmpty()) return QPair<QString, QVersionNumber>();
if (filenames.size() == 1) return QPair<QString, QVersionNumber>(filenames.first(), versionNumberFromFilename(filenames.first()));
QString latest;
QVersionNumber latestVersion;
for (const QString &fn : filenames)
{
if (latest.isEmpty())
{
latest = fn;
latestVersion = versionNumberFromFilename(fn);
continue;
}
const QVersionNumber version = versionNumberFromFilename(fn);
if (version > latestVersion)
{
latest = fn;
latestVersion = versionNumberFromFilename(fn);
}
}
return QPair<QString, QVersionNumber>(latest, latestVersion);
}
} // ns
} // ns

View File

@@ -0,0 +1,146 @@
/* Copyright (C) 2017
* swift project community / contributors
*
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
* directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project,
* including this file, may be copied, modified, propagated, or distributed except according to the terms
* contained in the LICENSE file.
*/
//! \file
#ifndef BLACKMISC_DB_DISTRIBUTION_H
#define BLACKMISC_DB_DISTRIBUTION_H
#include "blackmisc/blackmiscexport.h"
#include "blackmisc/network/urllist.h"
#include "blackmisc/db/datastore.h"
#include "blackmisc/settingscache.h"
#include "blackmisc/dictionary.h"
#include "blackmisc/valueobject.h"
#include "blackmisc/variant.h"
#include <QMetaType>
#include <QVersionNumber>
#include <QString>
namespace BlackMisc
{
namespace Db
{
//! Dictionary for files per platform
using CPlatformDictionary = BlackMisc::CDictionary<QString, QString>;
//! CDistribution for channel
class BLACKMISC_EXPORT CDistribution :
public BlackMisc::CValueObject<CDistribution>,
public BlackMisc::Db::IDatastoreObjectWithIntegerKey
{
public:
//! Properties by index
enum ColumnIndex
{
IndexChannel = BlackMisc::CPropertyIndex::GlobalIndexCDistribution,
IndexRestricted,
IndexDownloadUrls,
IndexPlatforms,
IndexPlatformFiles,
};
//! Default constructor
CDistribution();
//! Constructor
CDistribution(const QString &channel, bool restricted);
//! Destructor.
~CDistribution() {}
//! Version channel (Alpha, Beta, Stable ..)
const QString &getChannel() const { return m_channel; }
//! Set the channel
void setChannel(const QString &channel);
//! Get platforms
QStringList getPlatforms() const;
//! Guess platform
QString guessPlatform() const;
//! Version for platform
QString getVersionString(const QString &platform) const;
//! Version as QVersion
QVersionNumber getQVersion(const QString &platform) const;
//! File for platform
QString getFilename(const QString &platform) const;
//! Download URLs, i.e. here one can download installer
const BlackMisc::Network::CUrlList &getDownloadUrls() const { return m_downloadUrls; }
//! Add URL, ignored if empty
void addDownloadUrl(const BlackMisc::Network::CUrl &url);
//! At least one download URL?
bool hasDownloadUrls() const;
//! Restricted channel?
bool isRestricted() const { return m_restricted; }
//! \copydoc BlackMisc::Mixin::String::toQString
QString convertToQString(bool i18n = false) const;
//! To string
QString convertToQString(const QString &separator, bool i18n = false) const;
//! \copydoc BlackMisc::Mixin::Index::propertyByIndex
BlackMisc::CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const;
//! \copydoc BlackMisc::Mixin::Index::setPropertyByIndex
void setPropertyByIndex(const BlackMisc::CPropertyIndex &index, const BlackMisc::CVariant &variant);
//! Object from database JSON format
static CDistribution fromDatabaseJson(const QJsonObject &json, const QString &prefix = {});
private:
QString m_channel; //!< for development
bool m_restricted = false; //!< restricted access (i.e. password for download needed)
BlackMisc::Network::CUrlList m_downloadUrls; //!< download URLs, here I get the installer
CPlatformDictionary m_platformFiles; //!< the latest file version per platform
CPlatformDictionary m_platformVersions; //!< the version per platform
//! Extract version number from a file name
static QVersionNumber versionNumberFromFilename(const QString &filename);
//! Find the file representing the latest version
static QPair<QString, QVersionNumber> findLatestVersion(const QStringList &filenames);
BLACK_METACLASS(
CDistribution,
BLACK_METAMEMBER(dbKey),
BLACK_METAMEMBER(timestampMSecsSinceEpoch),
BLACK_METAMEMBER(channel),
BLACK_METAMEMBER(downloadUrls),
BLACK_METAMEMBER(platformFiles, 0, DisabledForComparison | DisabledForHashing),
BLACK_METAMEMBER(platformVersions, 0, DisabledForComparison | DisabledForHashing)
);
};
//! Distribution settings: channel/platform
struct TDistributionSetting : public BlackMisc::TSettingTrait<QStringList>
{
//! \copydoc BlackMisc::TSettingTrait::key
static const char *key() { return "distribution"; }
//! \copydoc BlackMisc::TSettingTrait::defaultValue
static QStringList defaultValue() { static const QStringList d{"", ""}; return d; }
};
} // ns
} // ns
Q_DECLARE_METATYPE(BlackMisc::Db::CDistribution)
Q_DECLARE_METATYPE(BlackMisc::Db::CPlatformDictionary)
#endif // guard

View File

@@ -0,0 +1,50 @@
/* Copyright (C) 2017
* swift Project Community / Contributors
*
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
* directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project,
* including this file, may be copied, modified, propagated, or distributed except according to the terms
* contained in the LICENSE file.
*/
#include "distributionlist.h"
#include "blackmisc/stringutils.h"
namespace BlackMisc
{
namespace Db
{
CDistributionList::CDistributionList() { }
CDistributionList::CDistributionList(const CSequence<CDistribution> &other) :
CSequence<CDistribution>(other)
{ }
QStringList CDistributionList::getChannels() const
{
QStringList channels;
for (const CDistribution &distribution : *this)
{
if (distribution.getChannel().isEmpty()) { continue; }
channels << distribution.getChannel();
}
return channels;
}
CDistribution CDistributionList::findByChannelOrDefault(const QString &channel) const
{
return this->findFirstByOrDefault(&CDistribution::getChannel, channel);
}
CDistributionList CDistributionList::fromDatabaseJson(const QJsonArray &array)
{
CDistributionList distributions;
for (const QJsonValue &value : array)
{
const CDistribution distribution(CDistribution::fromDatabaseJson(value.toObject()));
distributions.push_back(distribution);
}
return distributions;
}
} // namespace
} // namespace

View File

@@ -0,0 +1,68 @@
/* Copyright (C) 2017
* swift project Community / Contributors
*
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
* directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project,
* including this file, may be copied, modified, propagated, or distributed except according to the terms
* contained in the LICENSE file.
*/
//! \file
#ifndef BLACKMISC_DB_DISTRIBUTIONLIST_H
#define BLACKMISC_DB_DISTRIBUTIONLIST_H
#include "distribution.h"
#include "blackmisc/db/datastoreobjectlist.h"
#include "blackmisc/datacache.h"
#include "blackmisc/blackmiscexport.h"
#include "blackmisc/collection.h"
#include "blackmisc/sequence.h"
#include "blackmisc/variant.h"
namespace BlackMisc
{
namespace Db
{
//! Value object encapsulating a list of aircraft models
class BLACKMISC_EXPORT CDistributionList :
public BlackMisc::CSequence<CDistribution>,
public BlackMisc::Db::IDatastoreObjectList<CDistribution, CDistributionList, int>,
public BlackMisc::Mixin::MetaType<CDistributionList>
{
public:
BLACKMISC_DECLARE_USING_MIXIN_METATYPE(CDistributionList)
//! Empty constructor.
CDistributionList();
//! Construct from a base class object.
CDistributionList(const CSequence<CDistribution> &other);
//! All channels
QStringList getChannels() const;
//! Find distribution by channels
CDistribution findByChannelOrDefault(const QString &channel) const;
//! From database JSON
static CDistributionList fromDatabaseJson(const QJsonArray &array);
};
//! Trait for global setup data
struct TDistributionInfo : public BlackMisc::TDataTrait<CDistributionList>
{
//! Key in data cache
static const char *key() { return "distributions"; }
//! First load is synchronous
static constexpr bool isPinned() { return true; }
};
} // ns
} // ns
Q_DECLARE_METATYPE(BlackMisc::Db::CDistributionList)
Q_DECLARE_METATYPE(BlackMisc::CCollection<BlackMisc::Db::CDistribution>)
Q_DECLARE_METATYPE(BlackMisc::CSequence<BlackMisc::Db::CDistribution>)
#endif //guard

View File

@@ -19,6 +19,8 @@ namespace BlackMisc
CDbInfo::registerMetadata();
CDbInfoList::registerMetadata();
CDbFlags::registerMetadata();
CDistribution::registerMetadata();
CDistributionList::registerMetadata();
}
} // ns
} // ns

View File

@@ -137,7 +137,7 @@ namespace BlackMisc
GlobalIndexIDatastoreString = 11100,
GlobalIndexCDbInfo = 11200,
GlobalIndexCGlobalSetup = 12000,
GlobalIndexCUpdateInfo = 12100,
GlobalIndexCDistribution = 12100,
GlobalIndexCVatsimSetup = 12200,
GlobalIndexCLauncherSetup = 12300,
GlobalIndexCGuiStateDbOwnModelsComponent = 14000,

View File

@@ -15,6 +15,7 @@
#include "blackmisc/aviation/airport.h"
#include "blackmisc/aviation/airportlist.h"
#include "blackmisc/db/dbinfolist.h"
#include "blackmisc/db/distributionlist.h"
#include "blackmisc/network/textmessage.h"
#include "blackmisc/network/textmessagelist.h"
#include "blackmisc/simulation/distributorlist.h"
@@ -114,6 +115,15 @@ namespace BlackMisc
}
}
template<class OBJ, class CONTAINER>
void ITimestampObjectList<OBJ, CONTAINER>::setUtcTime(qint64 msSinceEpoch)
{
for (ITimestampBased &tsObj : this->container())
{
tsObj.setMSecsSinceEpoch(msSinceEpoch);
}
}
template <class OBJ, class CONTAINER>
void ITimestampObjectList<OBJ, CONTAINER>::setInvalidTimestampsToCurrentUtcTime()
{
@@ -236,6 +246,7 @@ namespace BlackMisc
template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList<BlackMisc::Aviation::CAirport, BlackMisc::Aviation::CAirportList>;
template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList<BlackMisc::Aviation::CLivery, BlackMisc::Aviation::CLiveryList>;
template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList<BlackMisc::Db::CDbInfo, BlackMisc::Db::CDbInfoList>;
template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList<BlackMisc::Db::CDistribution, BlackMisc::Db::CDistributionList>;
template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList<BlackMisc::Network::CTextMessage, BlackMisc::Network::CTextMessageList>;
template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList<BlackMisc::Simulation::CAircraftModel, BlackMisc::Simulation::CAircraftModelList>;
template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList<BlackMisc::Simulation::CDistributor, BlackMisc::Simulation::CDistributorList>;

View File

@@ -53,6 +53,9 @@ namespace BlackMisc
//! Set all timestamps to now
void setCurrentUtcTime();
//! Set all timestamps to given time
void setUtcTime(qint64 msSinceEpoch);
//! Set invalid timestamps to now
void setInvalidTimestampsToCurrentUtcTime();
@@ -130,6 +133,8 @@ namespace BlackMisc
{
class CDbInfo;
class CDbInfoList;
class CDistribution;
class CDistributionList;
}
namespace Simulation
@@ -154,6 +159,7 @@ namespace BlackMisc
extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList<BlackMisc::Aviation::CAirlineIcaoCode, BlackMisc::Aviation::CAirlineIcaoCodeList>;
extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList<BlackMisc::Aviation::CAirport, BlackMisc::Aviation::CAirportList>;
extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList<BlackMisc::Db::CDbInfo, BlackMisc::Db::CDbInfoList>;
extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList<BlackMisc::Db::CDistribution, BlackMisc::Db::CDistributionList>;
extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList<BlackMisc::Simulation::CAircraftModel, BlackMisc::Simulation::CAircraftModelList>;
extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList<BlackMisc::Simulation::CDistributor, BlackMisc::Simulation::CDistributorList>;
extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList<BlackMisc::Network::CTextMessage, BlackMisc::Network::CTextMessageList>;