HaikuDepot: Refactor for general performance

This commit is a large "big bang" commit of a composite of changes
that would have been challenging to put in place over a PR train.
The changes yield a significant performance improvement in general
but also lay the foundation for being able to make further smaller
gains over time. The changes should also improve stability of the
application. This commit is a high-risk change and would ideally
be coupled with testing.

The key things which have changed in this commit;

- Code reformatting has been applied at the same time.
- Data models are now largely immutable with builder-style
  constructors.
  - The code base was previously handling locking of data obtained
    from the central model manually in many places which was
    prone to mistakes. Now accessing data is, from the clients'
    perspective, largely lock-free.
  - The removal of locks now removes lock-contention in various
    areas of the application including startup which helps with
    improving performance.
  - Repositories of data such as categories and languages are
    now also immutable.
- To simplify data immutability, packages' data is stored in the
  central model separately from depots' data.
- Large package data mutations are now done in chunks of packages
  rather than package-by-package which means reduced messaging
  within the application; especially around the GUI.
- The resolution of data / mutations' notifications is larger
  so that changes are not being considered on each property of
  a package which simplifies the processing logic in a number of
  places.
- Package change-notification is driven out of the central model
  rather than from each and every package. There is scope for
  further simplification in this space.
- Where logic already knows what data is changed, it is able to
  control the change-notification process rather than cause a
  large number of object-comparisons to be required.
- The data-loaders from the local package manager and from HDS
  have been re-written to work with immutable models.
- The icon repositories have been re-written to make a one-time
  notification that icons have changed rather than notify for
  every package.
- Data updates reflection in the UI has been changed to work in
  chunks of packages rather than package by package which
  reduces the quantity of UI window-invalidation / redraws and
  also reduces the quantity of re-calculation of scrollbars and
  other UI elements.
- Some UI elements have their metrics calculated lazily rather
  than up-front which yields a significant performance gain.

Change-Id: Iefaf4a137411e379e4c461ebc8c783fd1646bcf4
Reviewed-on: https://review.haiku-os.org/c/haiku/+/9093
Tested-by: Commit checker robot <[email protected]>
Reviewed-by: Andrew Lindesay <[email protected]>
This commit is contained in:
Andrew Lindesay
2025-03-15 06:56:32 +00:00
parent 20a6449385
commit 05ff00f04c
125 changed files with 7445 additions and 5037 deletions
+1 -1
View File
@@ -8,7 +8,7 @@ resource app_flags B_SINGLE_LAUNCH;
resource app_version { resource app_version {
major = 0, major = 0,
middle = 0, middle = 0,
minor = 8, minor = 9,
variety = B_APPV_ALPHA, variety = B_APPV_ALPHA,
internal = 1, internal = 1,
+11 -1
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2024, Andrew Lindesay <[email protected]>. * Copyright 2018-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef HAIKU_DEPOT_CONSTANTS_H #ifndef HAIKU_DEPOT_CONSTANTS_H
@@ -118,9 +118,19 @@ enum UserUsageConditionsSelectionMode {
// any prominence ordering value greater than this is not prominent. // any prominence ordering value greater than this is not prominent.
#define PROMINANCE_ORDERING_MAX 1000 #define PROMINANCE_ORDERING_MAX 1000
// this is the highest prominence value possible. // this is the highest prominence value possible.
#define PROMINANCE_ORDERING_NONE 0
#define SINGLE_PACKAGE_DEPOT_NAME "__hd_single_pkg_depot" #define SINGLE_PACKAGE_DEPOT_NAME "__hd_single_pkg_depot"
// used as a fake depot name for the case where the application is looking // used as a fake depot name for the case where the application is looking
// at a single package based on a file. // at a single package based on a file.
#define SINGLE_PACKAGE_DEPOT_IDENTIFIER "__hd_single_pkg_depot_id_c07fb16620f4"
// used as a fake depot name for the case where the application is looking
// at a single package based on a file.
#define LOCAL_DEPOT_IDENTIFIER "__hd_local_depot_id_008922773013"
// used as a fake depot identifier that is a placeholder used to convey
// packages that are only installed locally and do not exist in a remote
// repository.
#endif // HAIKU_DEPOT_CONSTANTS_H #endif // HAIKU_DEPOT_CONSTANTS_H
+9 -7
View File
@@ -57,6 +57,7 @@ local dumpExportReferenceModelTargetFile =
# source directories # source directories
local sourceDirs = local sourceDirs =
edits_generic edits_generic
icon
model model
packagemanagement packagemanagement
packagemodel packagemodel
@@ -124,21 +125,18 @@ local applicationSources =
FeaturedPackagesView.cpp FeaturedPackagesView.cpp
FilterView.cpp FilterView.cpp
GeneralContentScrollView.cpp GeneralContentScrollView.cpp
IconTarPtr.cpp IdentityAndAccessUtils.cpp
IncrementViewCounterProcess.cpp IncrementViewCounterProcess.cpp
JobStateListener.cpp JobStateListener.cpp
JwtTokenHelper.cpp JwtTokenHelper.cpp
LanguageRepository.cpp
LinkView.cpp LinkView.cpp
LinkedBitmapView.cpp LinkedBitmapView.cpp
Logger.cpp Logger.cpp
main.cpp main.cpp
MainWindow.cpp MainWindow.cpp
MarkupTextView.cpp MarkupTextView.cpp
MessagePackageListener.cpp
Model.cpp Model.cpp
PackageContentsView.cpp PackageContentsView.cpp
PackageIconTarRepository.cpp
PackageInfoListener.cpp PackageInfoListener.cpp
PackageInfoView.cpp PackageInfoView.cpp
PackageListView.cpp PackageListView.cpp
@@ -165,6 +163,11 @@ local applicationSources =
ValidationUtils.cpp ValidationUtils.cpp
WorkStatusView.cpp WorkStatusView.cpp
# icon
IconTarPtr.cpp
PackageIconDefaultRepository.cpp
PackageIconTarRepository.cpp
# packagemanagement # packagemanagement
AbstractPackageProcess.cpp AbstractPackageProcess.cpp
InstallPackageProcess.cpp InstallPackageProcess.cpp
@@ -180,17 +183,17 @@ local applicationSources =
PackageClassificationInfo.cpp PackageClassificationInfo.cpp
PackageCoreInfo.cpp PackageCoreInfo.cpp
PackageFilter.cpp PackageFilter.cpp
PackageFilterModel.cpp PackageFilterSpecification.cpp
PackageInfo.cpp PackageInfo.cpp
PackageLocalInfo.cpp PackageLocalInfo.cpp
PackageLocalizedText.cpp PackageLocalizedText.cpp
PackagePublisherInfo.cpp PackagePublisherInfo.cpp
PackageScreenshotInfo.cpp PackageScreenshotInfo.cpp
PackageUserRatingInfo.cpp
PackageVersion.cpp PackageVersion.cpp
ScreenshotInfo.cpp ScreenshotInfo.cpp
UserInfo.cpp UserInfo.cpp
UserRating.cpp UserRating.cpp
UserRatingInfo.cpp
UserRatingSummary.cpp UserRatingSummary.cpp
# network + server / local processes # network + server / local processes
@@ -231,7 +234,6 @@ local applicationSources =
PackageUtils.cpp PackageUtils.cpp
StorageUtils.cpp StorageUtils.cpp
StringUtils.cpp StringUtils.cpp
LoggingUrlProtocolListener.cpp
# package_daemon # package_daemon
ProblemWindow.cpp ProblemWindow.cpp
@@ -1,7 +1,7 @@
#!/usr/bin/python #!/usr/bin/python
# ===================================== # =====================================
# Copyright 2017-2023, Andrew Lindesay # Copyright 2017-2025, Andrew Lindesay
# Distributed under the terms of the MIT License. # Distributed under the terms of the MIT License.
# ===================================== # =====================================
@@ -38,21 +38,21 @@ public:
void Reset(); void Reset();
{{#propertyarray}}{{#property.iscppscalartype}} {{#propertyarray}}{{#property.iscppscalartype}}
{{property.cpptype}} {{property.cppname}}(); {{property.cpptype}} {{property.cppname}}() const;
void Set{{property.cppname}}({{property.cpptype}} value); void Set{{property.cppname}}({{property.cpptype}} value);
void Set{{property.cppname}}Null(); void Set{{property.cppname}}Null();
bool {{property.cppname}}IsNull(); bool {{property.cppname}}IsNull() const;
{{/property.iscppscalartype}}{{#property.isarray}} {{/property.iscppscalartype}}{{#property.isarray}}
void AddTo{{property.cppname}}({{property.items.cpptype}}* value); void AddTo{{property.cppname}}({{property.items.cpptype}}* value);
void Set{{property.cppname}}({{property.cpptype}}* value); void Set{{property.cppname}}({{property.cpptype}}* value);
int32 Count{{property.cppname}}(); int32 Count{{property.cppname}}() const;
{{property.items.cpptype}}* {{property.cppname}}ItemAt(int32 index); {{property.items.cpptype}}* {{property.cppname}}ItemAt(int32 index) const;
bool {{property.cppname}}IsNull(); bool {{property.cppname}}IsNull() const;
{{/property.isarray}}{{#property.iscppnonscalarnoncollectiontype}} {{/property.isarray}}{{#property.iscppnonscalarnoncollectiontype}}
{{property.cpptype}}* {{property.cppname}}(); {{property.cpptype}}* {{property.cppname}}() const;
void Set{{property.cppname}}({{property.cpptype}}* value); void Set{{property.cppname}}({{property.cpptype}}* value);
void Set{{property.cppname}}Null(); void Set{{property.cppname}}Null();
bool {{property.cppname}}IsNull(); bool {{property.cppname}}IsNull() const;
{{/property.iscppnonscalarnoncollectiontype}} {{/property.iscppnonscalarnoncollectiontype}}
{{/propertyarray}} {{/propertyarray}}
@@ -112,7 +112,7 @@ void
} }
{{#propertyarray}}{{#property.iscppscalartype}}{{property.cpptype}} {{#propertyarray}}{{#property.iscppscalartype}}{{property.cpptype}}
{{cppobjectname}}::{{property.cppname}}() {{cppobjectname}}::{{property.cppname}}() const
{ {
return {{property.cppmembername}}; return {{property.cppmembername}};
} }
@@ -135,7 +135,7 @@ void
bool bool
{{cppobjectname}}::{{property.cppname}}IsNull() {{cppobjectname}}::{{property.cppname}}IsNull() const
{ {
return 0 == (fHasValueBitmask & k{{property.cppname}}Bitmask); return 0 == (fHasValueBitmask & k{{property.cppname}}Bitmask);
} }
@@ -160,7 +160,7 @@ void
int32 int32
{{cppobjectname}}::Count{{property.cppname}}() {{cppobjectname}}::Count{{property.cppname}}() const
{ {
if ({{property.cppmembername}} == NULL) if ({{property.cppmembername}} == NULL)
return 0; return 0;
@@ -169,20 +169,20 @@ int32
{{property.items.cpptype}}* {{property.items.cpptype}}*
{{cppobjectname}}::{{property.cppname}}ItemAt(int32 index) {{cppobjectname}}::{{property.cppname}}ItemAt(int32 index) const
{ {
return {{property.cppmembername}}->ItemAt(index); return {{property.cppmembername}}->ItemAt(index);
} }
bool bool
{{cppobjectname}}::{{property.cppname}}IsNull() {{cppobjectname}}::{{property.cppname}}IsNull() const
{ {
return {{property.cppmembername}} == NULL; return {{property.cppmembername}} == NULL;
} }
{{/property.isarray}}{{#property.iscppnonscalarnoncollectiontype}}{{property.cpptype}}* {{/property.isarray}}{{#property.iscppnonscalarnoncollectiontype}}{{property.cpptype}}*
{{cppobjectname}}::{{property.cppname}}() {{cppobjectname}}::{{property.cppname}}() const
{ {
return {{property.cppmembername}}; return {{property.cppmembername}};
} }
@@ -206,7 +206,7 @@ void
bool bool
{{cppobjectname}}::{{property.cppname}}IsNull() {{cppobjectname}}::{{property.cppname}}IsNull() const
{ {
return {{property.cppmembername}} == NULL; return {{property.cppmembername}} == NULL;
} }
@@ -0,0 +1,104 @@
/*
* Copyright 2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "PackageIconDefaultRepository.h"
#include <IconUtils.h>
#include "Logger.h"
static const int32 kCacheLimit = 50;
PackageIconDefaultRepository::PackageIconDefaultRepository()
:
fVectorData(NULL),
fVectorDataSize(0),
fCache(kCacheLimit)
{
_InitDefaultVectorIcon();
}
PackageIconDefaultRepository::~PackageIconDefaultRepository()
{
delete fVectorData;
}
status_t
PackageIconDefaultRepository::GetIcon(const BString& pkgName, uint32 size,
BitmapHolderRef& bitmapHolderRef)
{
if (fVectorData == NULL)
return B_NOT_INITIALIZED;
bitmapHolderRef.Unset();
status_t status = B_OK;
HashString key(BString() << size);
if (!fCache.ContainsKey(key)) {
BBitmap* bitmap = NULL;
if (status == B_OK) {
bitmap = new BBitmap(BRect(0, 0, size - 1, size - 1), 0, B_RGBA32);
status = bitmap->InitCheck();
}
if (status == B_OK)
status = BIconUtils::GetVectorIcon(fVectorData, fVectorDataSize, bitmap);
if (status == B_OK) {
HDINFO("did create default package icon size %" B_PRId32, size);
BitmapHolderRef bitmapHolder(new(std::nothrow) BitmapHolder(bitmap), true);
fCache.Put(key, bitmapHolder);
} else {
delete bitmap;
bitmap = NULL;
}
}
if (status == B_OK)
bitmapHolderRef.SetTo(fCache.Get(key).Get());
else
HDERROR("failed to create default package icon size %" B_PRId32, size);
return status;
}
void
PackageIconDefaultRepository::_InitDefaultVectorIcon()
{
if (fVectorData != NULL) {
delete fVectorData;
fVectorData = NULL;
}
fVectorDataSize = 0;
BMimeType mimeType("application/x-vnd.haiku-package");
status_t status = mimeType.InitCheck();
if (status != B_OK)
return;
uint8* data;
size_t dataSize;
if (mimeType.GetIcon(&data, &dataSize) != B_OK)
debugger("the default icon was unable to be acquired");
fVectorData = new(std::nothrow) uint8[dataSize];
if (fVectorData == NULL)
HDFATAL("unable to allocate memory for the default icon");
memcpy(fVectorData, data, dataSize);
fVectorDataSize = dataSize;
}
@@ -0,0 +1,36 @@
/*
* Copyright 2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PACKAGE_ICON_DEFAULT_REPOSITORY_H
#define PACKAGE_ICON_DEFAULT_REPOSITORY_H
#include <HashMap.h>
#include <HashString.h>
#include "LRUCache.h"
#include "PackageIconRepository.h"
class PackageIconDefaultRepository : public PackageIconRepository {
public:
PackageIconDefaultRepository();
virtual ~PackageIconDefaultRepository();
virtual status_t GetIcon(const BString& pkgName, uint32 size,
BitmapHolderRef& bitmapHolderRef);
private:
void _InitDefaultVectorIcon();
private:
uint8* fVectorData;
size_t fVectorDataSize;
LRUCache<HashString, BitmapHolderRef>
fCache;
};
#endif // PACKAGE_ICON_DEFAULT_REPOSITORY_H
@@ -1,5 +1,5 @@
/* /*
* Copyright 2020-2024, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2020-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef PACKAGE_ICON_REPOSITORY_H #ifndef PACKAGE_ICON_REPOSITORY_H
@@ -11,13 +11,14 @@
#include "HaikuDepotConstants.h" #include "HaikuDepotConstants.h"
class PackageIconRepository { class PackageIconRepository : public BReferenceable {
public: public:
virtual bool HasAnyIcon(const BString& pkgName) = 0;
virtual status_t GetIcon(const BString& pkgName, uint32 size, virtual status_t GetIcon(const BString& pkgName, uint32 size,
BitmapHolderRef& bitmapHolderRef) = 0; BitmapHolderRef& bitmapHolderRef) = 0;
virtual void Clear() = 0;
}; };
typedef BReference<PackageIconRepository> PackageIconRepositoryRef;
#endif // PACKAGE_ICON_REPOSITORY_H #endif // PACKAGE_ICON_REPOSITORY_H
@@ -1,11 +1,10 @@
/* /*
* Copyright 2020-2024, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2020-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#include "PackageIconTarRepository.h" #include "PackageIconTarRepository.h"
#include <AutoDeleter.h> #include <AutoDeleter.h>
#include <Autolock.h>
#include <File.h> #include <File.h>
#include <IconUtils.h> #include <IconUtils.h>
#include <StopWatch.h> #include <StopWatch.h>
@@ -87,8 +86,7 @@ IconTarPtrEntryListener::Handle(const TarArchiveHeader& header,
BString packageName; BString packageName;
BString leafName; BString leafName;
fileName.CopyInto(packageName, 5, secondSlashIdx - 5); fileName.CopyInto(packageName, 5, secondSlashIdx - 5);
fileName.CopyInto(leafName, secondSlashIdx + 1, fileName.CopyInto(leafName, secondSlashIdx + 1, fileName.Length() - (secondSlashIdx + 1));
fileName.Length() - (secondSlashIdx + 1));
BitmapSize storedSize; BitmapSize storedSize;
if (_LeafNameToBitmapSize(leafName, &storedSize) == B_OK) if (_LeafNameToBitmapSize(leafName, &storedSize) == B_OK)
@@ -121,31 +119,21 @@ IconTarPtrEntryListener::_LeafNameToBitmapSize(BString& leafName, BitmapSize* st
} }
PackageIconTarRepository::PackageIconTarRepository() PackageIconTarRepository::PackageIconTarRepository(BPath& tarPath)
: :
fTarPath(tarPath),
fTarIo(NULL), fTarIo(NULL),
fIconCache(LIMIT_ICON_CACHE), fIconCache(LIMIT_ICON_CACHE),
fDefaultIconVectorData(NULL), fIconDataBuffer(new BMallocIO()),
fDefaultIconVectorDataSize(0), fFallbackRepository(PackageIconDefaultRepository())
fDefaultIconCache(LIMIT_ICON_CACHE),
fIconDataBuffer(new BMallocIO())
{ {
_InitDefaultVectorIcon();
} }
PackageIconTarRepository::~PackageIconTarRepository() PackageIconTarRepository::~PackageIconTarRepository()
{ {
_Close();
delete fIconDataBuffer; delete fIconDataBuffer;
delete fDefaultIconVectorData;
}
void
PackageIconTarRepository::Clear() {
BAutolock locker(&fLock);
fIconCache.Clear();
fDefaultIconCache.Clear();
} }
@@ -155,27 +143,25 @@ PackageIconTarRepository::Clear() {
*/ */
status_t status_t
PackageIconTarRepository::Init(BPath& tarPath) PackageIconTarRepository::Init()
{ {
BAutolock locker(&fLock);
_Close(); _Close();
status_t result = B_OK; status_t result = B_OK;
if (tarPath.Path() == NULL) { if (fTarPath.Path() == NULL) {
HDINFO("empty path to tar-ball"); HDINFO("empty path to tar-ball");
result = B_BAD_VALUE; result = B_BAD_VALUE;
} }
BFile *tarIo = NULL; BFile* tarIo = NULL;
if (result == B_OK) { if (result == B_OK) {
HDINFO("will init icon model from tar [%s]", tarPath.Path()); HDINFO("will init icon model from tar [%s]", fTarPath.Path());
tarIo = new BFile(tarPath.Path(), O_RDONLY); tarIo = new BFile(fTarPath.Path(), O_RDONLY);
if (!tarIo->IsReadable()) { if (!tarIo->IsReadable()) {
HDERROR("unable to read the tar [%s]", tarPath.Path()); HDERROR("unable to read the tar [%s]", fTarPath.Path());
result = B_IO_ERROR; result = B_IO_ERROR;
} }
} }
@@ -184,15 +170,14 @@ PackageIconTarRepository::Init(BPath& tarPath)
if (result == B_OK) { if (result == B_OK) {
BStopWatch watch("PackageIconTarRepository::Init", true); BStopWatch watch("PackageIconTarRepository::Init", true);
HDINFO("will read [%s] and collect the tar pointers", tarPath.Path()); HDINFO("will read [%s] and collect the tar pointers", fTarPath.Path());
IconTarPtrEntryListener* listener = new IconTarPtrEntryListener(this); IconTarPtrEntryListener* listener = new IconTarPtrEntryListener(this);
ObjectDeleter<IconTarPtrEntryListener> listenerDeleter(listener); ObjectDeleter<IconTarPtrEntryListener> listenerDeleter(listener);
TarArchiveService::ForEachEntry(*tarIo, listener); TarArchiveService::ForEachEntry(*tarIo, listener);
double secs = watch.ElapsedTime() / 1000000.0; double secs = watch.ElapsedTime() / 1000000.0;
HDINFO("did collect %" B_PRIi32 " tar pointers (%6.3g secs)", HDINFO("did collect %" B_PRIi32 " tar pointers (%6.3g secs)", fIconTarPtrs.Size(), secs);
fIconTarPtrs.Size(), secs);
} }
if (result == B_OK) if (result == B_OK)
@@ -214,14 +199,13 @@ PackageIconTarRepository::_Close()
delete fTarIo; delete fTarIo;
fTarIo = NULL; fTarIo = NULL;
fIconTarPtrs.Clear(); fIconTarPtrs.Clear();
fDefaultIconCache.Clear();
} }
/*! This method should be treated private and only called from a situation /*! This method is used to populate data from the parsing of the tar headers.
in which the class's lock is acquired. It is used to populate data from It is called from the listener above.
the parsing of the tar headers. It is called from the listener above.
*/ */
// TODO; should be friend to the listener
void void
PackageIconTarRepository::AddIconTarPtr(const BString& packageName, BitmapSize storedSize, PackageIconTarRepository::AddIconTarPtr(const BString& packageName, BitmapSize storedSize,
off_t offset) off_t offset)
@@ -231,15 +215,6 @@ PackageIconTarRepository::AddIconTarPtr(const BString& packageName, BitmapSize s
} }
bool
PackageIconTarRepository::HasAnyIcon(const BString& pkgName)
{
BAutolock locker(&fLock);
HashString key(pkgName);
return fIconTarPtrs.ContainsKey(key);
}
status_t status_t
PackageIconTarRepository::GetIcon(const BString& pkgName, uint32 size, PackageIconTarRepository::GetIcon(const BString& pkgName, uint32 size,
BitmapHolderRef& bitmapHolderRef) BitmapHolderRef& bitmapHolderRef)
@@ -250,39 +225,44 @@ PackageIconTarRepository::GetIcon(const BString& pkgName, uint32 size,
return B_BAD_VALUE; return B_BAD_VALUE;
} }
status_t result = B_OK;
bitmapHolderRef.Unset(); bitmapHolderRef.Unset();
BAutolock locker(&fLock); if (fTarIo == NULL) {
status_t result = B_OK; // get the default icon in this case.
off_t iconDataTarOffset = -1; HDDEBUG("the tar io was not configured indicating a possible initialization error");
const IconTarPtrRef tarPtrRef = _GetIconTarPtr(pkgName); } else {
BitmapSize storedSize; off_t iconDataTarOffset = -1;
const IconTarPtrRef tarPtrRef = _GetIconTarPtr(pkgName);
BitmapSize storedSize;
if (tarPtrRef.IsSet()) { if (tarPtrRef.IsSet()) {
storedSize = _BestStoredSize(tarPtrRef, size); storedSize = _BestStoredSize(tarPtrRef, size);
iconDataTarOffset = tarPtrRef->Offset(storedSize); iconDataTarOffset = tarPtrRef->Offset(storedSize);
} }
if (iconDataTarOffset >= 0) { if (iconDataTarOffset >= 0) {
HashString key = _ToIconCacheKey(pkgName, storedSize, size); HashString key = _ToIconCacheKey(pkgName, storedSize, size);
if (fIconCache.ContainsKey(key)) { if (fIconCache.ContainsKey(key)) {
bitmapHolderRef.SetTo(fIconCache.Get(key).Get());
} else {
result = _CreateIconFromTarOffset(iconDataTarOffset, storedSize, size, bitmapHolderRef);
if (result == B_OK) {
HDTRACE("loaded package icon [%s] of size %" B_PRId32, pkgName.String(), size);
fIconCache.Put(key, bitmapHolderRef);
bitmapHolderRef.SetTo(fIconCache.Get(key).Get()); bitmapHolderRef.SetTo(fIconCache.Get(key).Get());
} else { } else {
HDERROR("failure to read image for package [%s] at offset %" result = _CreateIconFromTarOffset(iconDataTarOffset, storedSize, size,
B_PRIdOFF, pkgName.String(), iconDataTarOffset); bitmapHolderRef);
if (result == B_OK) {
HDTRACE("loaded package icon [%s] of size %" B_PRId32, pkgName.String(), size);
fIconCache.Put(key, bitmapHolderRef);
bitmapHolderRef.SetTo(fIconCache.Get(key).Get());
} else {
HDERROR("failure to read image for package [%s] at offset %" B_PRIdOFF,
pkgName.String(), iconDataTarOffset);
}
} }
} }
} }
if (!bitmapHolderRef.IsSet()) if (!bitmapHolderRef.IsSet())
result = _GetDefaultIcon(size, bitmapHolderRef); result = fFallbackRepository.GetIcon(pkgName, size, bitmapHolderRef);
return result; return result;
} }
@@ -324,8 +304,6 @@ PackageIconTarRepository::_ToIconCacheKey(const BString& pkgName, BitmapSize sto
} }
/*! This method must only be invoked with the class locked.
*/
status_t status_t
PackageIconTarRepository::_CreateIconFromTarOffset(off_t offset, BitmapSize storedSize, uint32 size, PackageIconTarRepository::_CreateIconFromTarOffset(off_t offset, BitmapSize storedSize, uint32 size,
BitmapHolderRef& bitmapHolderRef) BitmapHolderRef& bitmapHolderRef)
@@ -409,7 +387,6 @@ PackageIconTarRepository::_BestStoredSize(const IconTarPtrRef iconTarPtrRef, int
IconTarPtrRef IconTarPtrRef
PackageIconTarRepository::_GetOrCreateIconTarPtr(const BString& pkgName) PackageIconTarRepository::_GetOrCreateIconTarPtr(const BString& pkgName)
{ {
BAutolock locker(&fLock);
HashString key(pkgName); HashString key(pkgName);
if (!fIconTarPtrs.ContainsKey(key)) { if (!fIconTarPtrs.ContainsKey(key)) {
IconTarPtrRef value(new IconTarPtr(pkgName)); IconTarPtrRef value(new IconTarPtr(pkgName));
@@ -418,80 +395,3 @@ PackageIconTarRepository::_GetOrCreateIconTarPtr(const BString& pkgName)
} }
return fIconTarPtrs.Get(key); return fIconTarPtrs.Get(key);
} }
status_t
PackageIconTarRepository::_GetDefaultIcon(uint32 size, BitmapHolderRef& bitmapHolderRef)
{
if (fDefaultIconVectorData == NULL)
return B_NOT_INITIALIZED;
bitmapHolderRef.Unset();
status_t status = B_OK;
HashString key(BString() << size);
if (!fDefaultIconCache.ContainsKey(key)) {
BBitmap* bitmap = NULL;
if (status == B_OK) {
bitmap = new BBitmap(BRect(0, 0, size - 1, size - 1), 0, B_RGBA32);
status = bitmap->InitCheck();
}
if (status == B_OK) {
status = BIconUtils::GetVectorIcon(fDefaultIconVectorData, fDefaultIconVectorDataSize,
bitmap);
}
if (status == B_OK) {
HDINFO("did create default package icon size %" B_PRId32, size);
BitmapHolderRef bitmapHolder(new(std::nothrow) BitmapHolder(bitmap), true);
fDefaultIconCache.Put(key, bitmapHolder);
} else {
delete bitmap;
bitmap = NULL;
}
}
if (status == B_OK)
bitmapHolderRef.SetTo(fDefaultIconCache.Get(key).Get());
else
HDERROR("failed to create default package icon size %" B_PRId32, size);
return status;
}
void
PackageIconTarRepository::_InitDefaultVectorIcon()
{
if (fDefaultIconVectorData != NULL) {
delete fDefaultIconVectorData;
fDefaultIconVectorData = NULL;
}
fDefaultIconVectorDataSize = 0;
BMimeType mimeType("application/x-vnd.haiku-package");
status_t status = mimeType.InitCheck();
uint8* data;
size_t dataSize;
if (status == B_OK)
status = mimeType.GetIcon(&data, &dataSize);
if (status == B_OK) {
fDefaultIconVectorData = new(std::nothrow) uint8[dataSize];
if (fDefaultIconVectorData == NULL)
HDFATAL("unable to allocate memory for the default icon");
memcpy(fDefaultIconVectorData, data, dataSize);
fDefaultIconVectorDataSize = dataSize;
} else {
fDefaultIconVectorData = NULL;
fDefaultIconVectorDataSize = 0;
}
}
@@ -1,5 +1,5 @@
/* /*
* Copyright 2020-2024, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2020-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef PACKAGE_ICON_TAR_REPOSITORY_H #ifndef PACKAGE_ICON_TAR_REPOSITORY_H
@@ -9,12 +9,12 @@
#include <DataIO.h> #include <DataIO.h>
#include <HashMap.h> #include <HashMap.h>
#include <HashString.h> #include <HashString.h>
#include <Locker.h>
#include <Path.h> #include <Path.h>
#include <Referenceable.h> #include <Referenceable.h>
#include "IconTarPtr.h" #include "IconTarPtr.h"
#include "LRUCache.h" #include "LRUCache.h"
#include "PackageIconDefaultRepository.h"
#include "PackageIconRepository.h" #include "PackageIconRepository.h"
@@ -23,17 +23,15 @@ typedef BReference<IconTarPtr> IconTarPtrRef;
class PackageIconTarRepository : public PackageIconRepository { class PackageIconTarRepository : public PackageIconRepository {
public: public:
PackageIconTarRepository(); PackageIconTarRepository(BPath& tarPath);
virtual ~PackageIconTarRepository(); virtual ~PackageIconTarRepository();
status_t Init(BPath& tarPath); status_t Init();
void AddIconTarPtr(const BString& pkgName, void AddIconTarPtr(const BString& pkgName,
BitmapSize size, off_t offset); BitmapSize size, off_t offset);
virtual status_t GetIcon(const BString& pkgName, uint32 size, virtual status_t GetIcon(const BString& pkgName, uint32 size,
BitmapHolderRef& bitmapHolderRef); BitmapHolderRef& bitmapHolderRef);
virtual bool HasAnyIcon(const BString& pkgName);
virtual void Clear();
private: private:
void _Close(); void _Close();
@@ -48,27 +46,21 @@ private:
status_t _CreateIconFromTarOffset(off_t offset, BitmapSize bitmapSize, status_t _CreateIconFromTarOffset(off_t offset, BitmapSize bitmapSize,
uint32 size, BitmapHolderRef& bitmapHolderRef); uint32 size, BitmapHolderRef& bitmapHolderRef);
status_t _GetDefaultIcon(uint32 size, BitmapHolderRef& bitmapHolderRef);
static BitmapSize _BestStoredSize(const IconTarPtrRef iconTarPtrRef, static BitmapSize _BestStoredSize(const IconTarPtrRef iconTarPtrRef,
int32 desiredSize); int32 desiredSize);
void _InitDefaultVectorIcon();
private: private:
BLocker fLock; BPath fTarPath;
BPositionIO* fTarIo; BPositionIO* fTarIo;
LRUCache<HashString, BitmapHolderRef> LRUCache<HashString, BitmapHolderRef>
fIconCache; fIconCache;
HashMap<HashString, IconTarPtrRef> HashMap<HashString, IconTarPtrRef>
fIconTarPtrs; fIconTarPtrs;
uint8* fDefaultIconVectorData;
size_t fDefaultIconVectorDataSize;
LRUCache<HashString, BitmapHolderRef>
fDefaultIconCache;
BMallocIO* fIconDataBuffer; BMallocIO* fIconDataBuffer;
PackageIconDefaultRepository
fFallbackRepository;
}; };
#endif // PACKAGE_ICON_TAR_REPOSITORY_H #endif // PACKAGE_ICON_TAR_REPOSITORY_H
@@ -1,88 +0,0 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "LanguageRepository.h"
#include <algorithm>
#include "HaikuDepotConstants.h"
#include "Logger.h"
#include "StringUtils.h"
LanguageRepository::LanguageRepository()
{
}
LanguageRepository::~LanguageRepository()
{
}
void
LanguageRepository::Clear()
{
fLanguages.clear();
HDINFO("did clear the languages");
}
bool
LanguageRepository::IsEmpty() const
{
return fLanguages.empty();
}
int32
LanguageRepository::CountLanguages() const
{
return fLanguages.size();
}
const LanguageRef
LanguageRepository::LanguageAtIndex(int32 index) const
{
return fLanguages[index];
}
void
LanguageRepository::AddLanguage(const LanguageRef& value)
{
int32 index = IndexOfLanguage(value->Code(), value->CountryCode(), value->ScriptCode());
if (-1 == index) {
std::vector<LanguageRef>::iterator itInsertionPt
= std::lower_bound(fLanguages.begin(), fLanguages.end(), value, &IsLanguageBefore);
fLanguages.insert(itInsertionPt, value);
HDTRACE("did add the language [%s]", value->ID());
} else {
fLanguages[index] = value;
HDTRACE("did replace the language [%s]", value->ID());
}
}
int32
LanguageRepository::IndexOfLanguage(const char* code, const char* countryCode,
const char* scriptCode) const
{
size_t size = fLanguages.size();
for (uint32 i = 0; i < size; i++) {
const char* lCode = fLanguages[i]->Code();
const char* lCountryCode = fLanguages[i]->CountryCode();
const char* lScriptCode = fLanguages[i]->ScriptCode();
if (0 == StringUtils::NullSafeCompare(code, lCode)
&& 0 == StringUtils::NullSafeCompare(countryCode, lCountryCode)
&& 0 == StringUtils::NullSafeCompare(scriptCode, lScriptCode)) {
return i;
}
}
return -1;
}
@@ -1,34 +0,0 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef LANGUAGE_REPOSITORY_H
#define LANGUAGE_REPOSITORY_H
#include <vector>
#include "Language.h"
class LanguageRepository
{
public:
LanguageRepository();
virtual ~LanguageRepository();
void Clear();
bool IsEmpty() const;
int32 CountLanguages() const;
const LanguageRef LanguageAtIndex(int32 index) const;
void AddLanguage(const LanguageRef& value);
int32 IndexOfLanguage(const char* code, const char* countryCode,
const char* scriptCode) const;
private:
std::vector<LanguageRef>
fLanguages;
};
#endif // LANGUAGE_REPOSITORY_H
+415 -299
View File
@@ -1,16 +1,16 @@
/* /*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>. * Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2014, Axel Dörfler <[email protected]>. * Copyright 2014, Axel Dörfler <[email protected]>.
* Copyright 2016-2024, Andrew Lindesay <[email protected]>. * Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#include "Model.h" #include "Model.h"
#include <algorithm> #include <algorithm>
#include <ctime> #include <ctime>
#include <vector>
#include <stdarg.h> #include <stdarg.h>
#include <stdint.h>
#include <time.h> #include <time.h>
#include <Autolock.h> #include <Autolock.h>
@@ -35,11 +35,8 @@
#define B_TRANSLATION_CONTEXT "Model" #define B_TRANSLATION_CONTEXT "Model"
#define KEY_STORE_IDENTIFIER_PREFIX "hds.password." static const uint32 kChangeMaskAll = UINT32_MAX;
// this prefix is added before the nickname in the keystore // covers all of the possible values.
// so that HDS username/password pairs can be identified.
static const char* kHaikuDepotKeyring = "HaikuDepot";
ModelListener::~ModelListener() ModelListener::~ModelListener()
@@ -56,58 +53,107 @@ Model::Model()
fDepots(), fDepots(),
fCategories(), fCategories(),
fPackageListViewMode(PROMINENT), fPackageListViewMode(PROMINENT),
fCanShareAnonymousUsageData(false) fCanShareAnonymousUsageData(false),
fWebApp(WebAppInterfaceRef(new WebAppInterface(UserCredentials()), true)),
fLanguages(LocaleUtils::WellKnownLanguages())
{ {
fIconRepository = PackageIconRepositoryRef(new PackageIconDefaultRepository(), true);
// setup the language into a default state with a hard-coded language so fFilterSpecification = PackageFilterSpecificationBuilder().BuildRef();
// that there is a language at all. fFilter = PackageFilterFactory::CreateFilter(fFilterSpecification);
fLanguageRepository = new LanguageRepository();
fLanguageRepository->AddLanguage(fPreferredLanguage);
fPackageFilterModel = new PackageFilterModel(); fPackageScreenshotRepository
fPackageScreenshotRepository = new PackageScreenshotRepository( = new PackageScreenshotRepository(PackageScreenshotRepositoryListenerRef(this), this);
PackageScreenshotRepositoryListenerRef(this),
&fWebAppInterface);
} }
Model::~Model() Model::~Model()
{ {
delete fPackageFilterModel;
delete fPackageScreenshotRepository; delete fPackageScreenshotRepository;
delete fLanguageRepository;
} }
LanguageRepository* const std::vector<LanguageRef>
Model::Languages() Model::Languages() const
{ {
return fLanguageRepository; BAutolock locker(&fLock);
return fLanguages;
} }
PackageFilterModel* void
Model::PackageFilter() Model::SetLanguagesAndPreferred(const std::vector<LanguageRef>& value, const LanguageRef& preferred)
{ {
return fPackageFilterModel; if (value.empty())
HDFATAL("unable to set the languages to an empty list");
if (std::find(value.begin(), value.end(), preferred) == value.end())
HDFATAL("the preferred language is not in the list of languages");
BAutolock locker(&fLock);
fLanguages = value;
std::sort(fLanguages.begin(), fLanguages.end(), IsLanguageRefLess);
SetPreferredLanguage(preferred);
HDINFO("did configure %" B_PRIu32 " languages with default [%s]",
static_cast<uint32>(fLanguages.size()), preferred->ID());
} }
PackageIconRepository& void
Model::GetPackageIconRepository() Model::SetFilterSpecification(const PackageFilterSpecificationRef& value)
{ {
return fPackageIconRepository; BAutolock locker(&fLock);
if (value != fFilterSpecification) {
fFilterSpecification = value;
fFilter = PackageFilterFactory::CreateFilter(value);
_NotifyPackageFilterChanged();
}
} }
status_t const PackageFilterSpecificationRef
Model::InitPackageIconRepository() Model::FilterSpecification() const
{ {
BPath tarPath; BAutolock locker(&fLock);
status_t result = StorageUtils::IconTarPath(tarPath); return fFilterSpecification;
if (result == B_OK) }
result = fPackageIconRepository.Init(tarPath);
return result;
PackageFilterRef
Model::Filter() const
{
BAutolock locker(&fLock);
return fFilter;
}
PackageIconRepositoryRef
Model::IconRepository()
{
BAutolock locker(&fLock);
return fIconRepository;
}
void
Model::SetIconRepository(PackageIconRepositoryRef value)
{
BAutolock locker(&fLock);
fIconRepository = value;
_NotifyIconsChanged();
}
void
Model::_NotifyIconsChanged()
{
std::vector<ModelListenerRef>::const_iterator it;
for (it = fListeners.begin(); it != fListeners.end(); it++) {
const ModelListenerRef& listener = *it;
if (listener.IsSet())
listener->IconsChanged();
}
} }
@@ -125,101 +171,286 @@ Model::AddListener(const ModelListenerRef& listener)
} }
LanguageRef void
Model::AddPackageListener(const PackageInfoListenerRef& packageListener)
{
fPackageListeners.push_back(packageListener);
}
/*! This method will take the event info data stored in the supplied message
and turn it back into an instance of `PackageInfoEvents`. It is done in
this way because the lookup of the packages within each `PackageInfoEvent`
has to come from the model.
*/
status_t
Model::DearchiveInfoEvents(const BMessage* message, PackageInfoEvents& packageInfoEvents) const
{
BAutolock locker(&fLock);
int32 i = 0;
BMessage eventMessage;
BString packageName;
uint32 changeMask;
while (message->FindMessage(kPackageInfoEventsKey, i, &eventMessage) == B_OK) {
status_t result = B_OK;
if (result == B_OK)
result = eventMessage.FindString(kPackageInfoPackageNameKey, &packageName);
if (result == B_OK)
result = eventMessage.FindUInt32(kPackageInfoChangesKey, &changeMask);
if (result == B_OK) {
PackageInfoRef package = PackageForName(packageName);
if (package.IsSet()) {
PackageInfoEvent event(package, changeMask);
packageInfoEvents.AddEvent(event);
}
} else {
HDFATAL("broken event info found processing events");
// should not occur as the data assembly is entirely inside the application
}
i++;
}
return B_OK;
}
const LanguageRef
Model::PreferredLanguage() const Model::PreferredLanguage() const
{ {
BAutolock locker(&fLock);
return fPreferredLanguage; return fPreferredLanguage;
} }
void void
Model::SetPreferredLanguage(LanguageRef value) Model::SetPreferredLanguage(const LanguageRef& value)
{ {
if (value.IsSet()) { if (value.IsSet()) {
if (fPreferredLanguage != value) BAutolock locker(&fLock);
fPreferredLanguage = value; fPreferredLanguage = value;
} }
} }
// TODO; part of a wider change; cope with the package being in more than one
// depot
PackageInfoRef
Model::PackageForName(const BString& name)
{
std::vector<DepotInfoRef>::iterator it;
for (it = fDepots.begin(); it != fDepots.end(); it++) {
DepotInfoRef depotInfoRef = *it;
PackageInfoRef packageInfoRef = depotInfoRef->PackageByName(name);
if (packageInfoRef.Get() != NULL)
return packageInfoRef;
}
return PackageInfoRef();
}
void void
Model::MergeOrAddDepot(const DepotInfoRef& depot) Model::SetDepots(const DepotInfoRef& depot)
{ {
BString depotName = depot->Name(); BAutolock locker(&fLock);
for(uint32 i = 0; i < fDepots.size(); i++) { std::vector<DepotInfoRef> depots;
if (fDepots[i]->Name() == depotName) { depots.push_back(depot);
DepotInfoRef ersatzDepot(new DepotInfo(*(fDepots[i].Get())), true); SetDepots(depots);
ersatzDepot->SyncPackagesFromDepot(depot);
fDepots[i] = ersatzDepot;
return;
}
}
fDepots.push_back(depot);
} }
bool void
Model::HasDepot(const BString& name) const Model::SetDepots(const std::vector<DepotInfoRef>& depots)
{ {
return NULL != DepotForName(name).Get(); BAutolock locker(&fLock);
fDepots.clear();
std::vector<DepotInfoRef>::const_iterator it;
for (it = depots.begin(); it != depots.end(); it++) {
DepotInfoRef depot = *it;
if (!depot.IsSet()) {
HDFATAL("attempt to add an unset depot");
} else {
if (depot->Name().IsEmpty())
HDFATAL("depot has no name");
if (depot->Identifier().IsEmpty())
HDFATAL("depot has no identifier");
fDepots[depot->Name()] = depot;
}
}
}
const std::vector<DepotInfoRef>
Model::Depots() const
{
BAutolock locker(&fLock);
std::vector<DepotInfoRef> result;
std::map<BString, DepotInfoRef>::const_iterator it;
for (it = fDepots.begin(); it != fDepots.end(); it++)
result.push_back(it->second);
return result;
} }
const DepotInfoRef const DepotInfoRef
Model::DepotForName(const BString& name) const Model::DepotForName(const BString& name) const
{ {
if (name.IsEmpty()) BAutolock locker(&fLock);
return DepotInfoRef(); std::map<BString, DepotInfoRef>::const_iterator it = fDepots.find(name);
if (it != fDepots.end())
std::vector<DepotInfoRef>::const_iterator it; return it->second;
for (it = fDepots.begin(); it != fDepots.end(); it++) {
DepotInfoRef aDepot = *it;
if (aDepot->Name() == name)
return aDepot;
}
return DepotInfoRef(); return DepotInfoRef();
} }
int32 const DepotInfoRef
Model::CountDepots() const Model::DepotForIdentifier(const BString& identifier) const
{ {
return fDepots.size(); BAutolock locker(&fLock);
std::map<BString, DepotInfoRef>::const_iterator it;
for (it = fDepots.begin(); it != fDepots.end(); it++) {
if (it->second->Identifier() == identifier)
return it->second;
}
return DepotInfoRef();
} }
DepotInfoRef const std::vector<PackageInfoRef>
Model::DepotAtIndex(int32 index) const Model::FilteredPackages() const
{ {
return fDepots[index]; BAutolock locker(&fLock);
std::map<BString, PackageInfoRef>::const_iterator it;
std::vector<PackageInfoRef> result;
for (it = fPackages.begin(); it != fPackages.end(); it++) {
PackageInfoRef package = it->second;
if (fFilter->AcceptsPackage(package))
result.push_back(package);
}
return result;
}
const std::vector<PackageInfoRef>
Model::Packages() const
{
BAutolock locker(&fLock);
std::map<BString, PackageInfoRef>::const_iterator it;
std::vector<PackageInfoRef> result;
for (it = fPackages.begin(); it != fPackages.end(); it++)
result.push_back(it->second);
return result;
}
uint32
Model::_ChangeDiff(const PackageInfoRef& package)
{
uint32 changeMask = kChangeMaskAll;
const PackageInfoRef existingPackage = PackageForName(package->Name());
if (existingPackage.IsSet())
changeMask = package->ChangeMask(*(existingPackage.Get()));
return changeMask;
}
void
Model::AddPackage(const PackageInfoRef& package)
{
if (!package.IsSet())
HDFATAL("attempt to add an unset package");
BAutolock locker(&fLock);
uint32 changeMask = _ChangeDiff(package);
fPackages[package->Name()] = package;
_NotifyPackageChange(PackageInfoEvent(package, changeMask));
}
void
Model::AddPackages(const std::vector<PackageInfoRef>& packages)
{
if (packages.empty())
return;
BAutolock locker(&fLock);
PackageInfoEvents events;
std::vector<PackageInfoRef>::const_iterator it;
for (it = packages.begin(); it != packages.end(); it++) {
PackageInfoRef package = *it;
events.AddEvent(PackageInfoEvent(package, _ChangeDiff(package)));
fPackages[package->Name()] = package;
}
_NotifyPackageChanges(events);
}
void
Model::AddPackagesWithChange(const std::vector<PackageInfoRef>& packages, uint32 changeMask)
{
if (packages.empty())
return;
BAutolock locker(&fLock);
PackageInfoEvents events;
std::vector<PackageInfoRef>::const_iterator it;
for (it = packages.begin(); it != packages.end(); it++) {
PackageInfoRef package = *it;
events.AddEvent(PackageInfoEvent(package, changeMask));
fPackages[package->Name()] = package;
}
_NotifyPackageChanges(events);
}
const PackageInfoRef
Model::PackageForName(const BString& name) const
{
BAutolock locker(&fLock);
std::map<BString, PackageInfoRef>::const_iterator it = fPackages.find(name);
if (it != fPackages.end())
return it->second;
return PackageInfoRef();
}
bool
Model::HasPackage(const BString& packageName) const
{
BAutolock locker(&fLock);
if (fPackages.find(packageName) != fPackages.end())
return true;
return false;
} }
bool bool
Model::HasAnyProminentPackages() Model::HasAnyProminentPackages()
{ {
std::vector<DepotInfoRef>::iterator it; BAutolock locker(&fLock);
for (it = fDepots.begin(); it != fDepots.end(); it++) { std::map<BString, PackageInfoRef>::iterator it;
DepotInfoRef aDepot = *it;
if (aDepot->HasAnyProminentPackages()) for (it = fPackages.begin(); it != fPackages.end(); it++) {
return true; const PackageInfoRef package = it->second;
if (package.IsSet()) {
const PackageClassificationInfoRef classificationInfo
= package->PackageClassificationInfo();
if (classificationInfo.IsSet()) {
if (classificationInfo->IsProminent())
return true;
}
}
} }
return false; return false;
} }
@@ -227,47 +458,41 @@ Model::HasAnyProminentPackages()
void void
Model::Clear() Model::Clear()
{ {
GetPackageIconRepository().Clear(); BAutolock locker(&fLock);
fIconRepository = PackageIconRepositoryRef(new PackageIconDefaultRepository(), true);
fDepots.clear(); fDepots.clear();
fPopulatedPackageNames.MakeEmpty(); fPackages.clear();
} }
void package_list_view_mode
Model::SetStateForPackagesByName(BStringList& packageNames, PackageState state) Model::PackageListViewMode() const
{ {
for (int32 i = 0; i < packageNames.CountStrings(); i++) { BAutolock locker(&fLock);
BString packageName = packageNames.StringAt(i); return fPackageListViewMode;
PackageInfoRef packageInfo = PackageForName(packageName);
if (packageInfo.IsSet()) {
// TODO; make this immutable
PackageLocalInfoRef localInfo = PackageUtils::NewLocalInfo(packageInfo);
localInfo->SetState(state);
packageInfo->SetLocalInfo(localInfo);
HDINFO("did update package [%s] with state [%s]", packageName.String(),
PackageUtils::StateToString(state));
}
else {
HDINFO("was unable to find package [%s] so was not possible to set the state to [%s]",
packageName.String(), PackageUtils::StateToString(state));
}
}
} }
void void
Model::SetPackageListViewMode(package_list_view_mode mode) Model::SetPackageListViewMode(package_list_view_mode mode)
{ {
BAutolock locker(&fLock);
fPackageListViewMode = mode; fPackageListViewMode = mode;
} }
bool
Model::CanShareAnonymousUsageData() const
{
BAutolock locker(&fLock);
return fCanShareAnonymousUsageData;
}
void void
Model::SetCanShareAnonymousUsageData(bool value) Model::SetCanShareAnonymousUsageData(bool value)
{ {
BAutolock locker(&fLock);
fCanShareAnonymousUsageData = value; fCanShareAnonymousUsageData = value;
} }
@@ -293,104 +518,29 @@ Model::CanPopulatePackage(const PackageInfoRef& package)
} }
static void WebAppInterfaceRef
model_remove_key_for_user(const BString& nickname) Model::WebApp()
{ {
if (nickname.IsEmpty()) BAutolock locker(&fLock);
return; return fWebApp;
BKeyStore keyStore;
BPasswordKey key;
BString passwordIdentifier = BString(KEY_STORE_IDENTIFIER_PREFIX)
<< nickname;
status_t result = keyStore.GetKey(kHaikuDepotKeyring, B_KEY_TYPE_PASSWORD,
passwordIdentifier, key);
switch (result) {
case B_OK:
result = keyStore.RemoveKey(kHaikuDepotKeyring, key);
if (result != B_OK) {
HDERROR("error occurred when removing password for nickname "
"[%s] : %s", nickname.String(), strerror(result));
}
break;
case B_ENTRY_NOT_FOUND:
return;
default:
HDERROR("error occurred when finding password for nickname "
"[%s] : %s", nickname.String(), strerror(result));
break;
}
}
void
Model::SetNickname(BString nickname)
{
BString password;
BString existingNickname = Nickname();
// this happens when the user is logging out. Best to remove the password
// stored for the existing user since it is no longer required.
if (!existingNickname.IsEmpty() && nickname.IsEmpty())
model_remove_key_for_user(existingNickname);
if (nickname.Length() > 0) {
BPasswordKey key;
BKeyStore keyStore;
BString passwordIdentifier = BString(KEY_STORE_IDENTIFIER_PREFIX)
<< nickname;
if (keyStore.GetKey(kHaikuDepotKeyring, B_KEY_TYPE_PASSWORD,
passwordIdentifier, key) == B_OK) {
password = key.Password();
}
if (password.IsEmpty())
nickname = "";
}
SetCredentials(nickname, password, false);
} }
const BString& const BString&
Model::Nickname() Model::Nickname()
{ {
return fWebAppInterface.Nickname(); BAutolock locker(&fLock);
return fWebApp->Nickname();
} }
void void
Model::SetCredentials(const BString& nickname, const BString& passwordClear, Model::SetCredentials(const UserCredentials& credentials)
bool storePassword)
{ {
BString existingNickname = Nickname();
if (storePassword) {
// no point continuing to store the password for the previous user.
if (!existingNickname.IsEmpty())
model_remove_key_for_user(existingNickname);
// adding a key that is already there does not seem to override the
// existing key so the old key needs to be removed first.
if (!nickname.IsEmpty())
model_remove_key_for_user(nickname);
if (!nickname.IsEmpty() && !passwordClear.IsEmpty()) {
BString keyIdentifier = BString(KEY_STORE_IDENTIFIER_PREFIX)
<< nickname;
BPasswordKey key(passwordClear, B_KEY_PURPOSE_WEB, keyIdentifier);
BKeyStore keyStore;
keyStore.AddKeyring(kHaikuDepotKeyring);
keyStore.AddKey(kHaikuDepotKeyring, key);
}
}
BAutolock locker(&fLock); BAutolock locker(&fLock);
fWebAppInterface.SetCredentials(UserCredentials(nickname, passwordClear)); const BString existingNickname = Nickname();
fWebApp = WebAppInterfaceRef(new WebAppInterface(credentials));
if (nickname != existingNickname) if (credentials.Nickname() != existingNickname)
_NotifyAuthorizationChanged(); _NotifyAuthorizationChanged();
} }
@@ -398,6 +548,22 @@ Model::SetCredentials(const BString& nickname, const BString& passwordClear,
// #pragma mark - listener notification methods // #pragma mark - listener notification methods
/*! Assumes that the class is locked.
*/
void
Model::_NotifyPackageFilterChanged()
{
std::vector<ModelListenerRef>::const_iterator it;
for (it = fListeners.begin(); it != fListeners.end(); it++) {
const ModelListenerRef& listener = *it;
if (listener.IsSet())
listener->PackageFilterChanged();
}
}
/*! Assumes that the class is locked.
*/
void void
Model::_NotifyAuthorizationChanged() Model::_NotifyAuthorizationChanged()
{ {
@@ -423,8 +589,35 @@ Model::_NotifyCategoryListChanged()
void void
Model::_MaybeLogJsonRpcError(const BMessage &responsePayload, Model::_NotifyPackageChange(const PackageInfoEvent& event)
const char *sourceDescription) const {
std::vector<PackageInfoListenerRef>::const_iterator it;
for (it = fPackageListeners.begin(); it != fPackageListeners.end(); it++) {
const PackageInfoListenerRef& listener = *it;
if (listener.IsSet())
listener->PackagesChanged(PackageInfoEvents(event));
}
}
// TODO: future work to optimize how this is conveyed to the listener in one go.
void
Model::_NotifyPackageChanges(const PackageInfoEvents& events)
{
if (events.IsEmpty())
return;
std::vector<PackageInfoListenerRef>::const_iterator it;
for (it = fPackageListeners.begin(); it != fPackageListeners.end(); it++) {
const PackageInfoListenerRef& listener = *it;
if (listener.IsSet())
listener->PackagesChanged(events);
}
}
void
Model::_MaybeLogJsonRpcError(const BMessage& responsePayload, const char* sourceDescription) const
{ {
BMessage error; BMessage error;
BString errorMessage; BString errorMessage;
@@ -433,143 +626,66 @@ Model::_MaybeLogJsonRpcError(const BMessage &responsePayload,
if (responsePayload.FindMessage("error", &error) == B_OK if (responsePayload.FindMessage("error", &error) == B_OK
&& error.FindString("message", &errorMessage) == B_OK && error.FindString("message", &errorMessage) == B_OK
&& error.FindDouble("code", &errorCode) == B_OK) { && error.FindDouble("code", &errorCode) == B_OK) {
HDERROR("[%s] --> error : [%s] (%f)", sourceDescription, HDERROR("[%s] --> error : [%s] (%f)", sourceDescription, errorMessage.String(), errorCode);
errorMessage.String(), errorCode); } else {
} else
HDERROR("[%s] --> an undefined error has occurred", sourceDescription); HDERROR("[%s] --> an undefined error has occurred", sourceDescription);
}
} }
// #pragma mark - Rating Stabilities // #pragma mark - Rating Stabilities
int32 const std::vector<RatingStabilityRef>
Model::CountRatingStabilities() const Model::RatingStabilities() const
{ {
return fRatingStabilities.size(); BAutolock locker(&fLock);
} return fRatingStabilities;
RatingStabilityRef
Model::RatingStabilityByCode(BString& code) const
{
std::vector<RatingStabilityRef>::const_iterator it;
for (it = fRatingStabilities.begin(); it != fRatingStabilities.end();
it++) {
RatingStabilityRef aRatingStability = *it;
if (aRatingStability->Code() == code)
return aRatingStability;
}
return RatingStabilityRef();
}
RatingStabilityRef
Model::RatingStabilityAtIndex(int32 index) const
{
return fRatingStabilities[index];
} }
void void
Model::AddRatingStabilities(std::vector<RatingStabilityRef>& values) Model::SetRatingStabilities(const std::vector<RatingStabilityRef> value)
{ {
std::vector<RatingStabilityRef>::const_iterator it; BAutolock locker(&fLock);
for (it = values.begin(); it != values.end(); it++) fRatingStabilities = value;
_AddRatingStability(*it); std::sort(fRatingStabilities.begin(), fRatingStabilities.end(), IsRatingStabilityRefLess);
}
void
Model::_AddRatingStability(const RatingStabilityRef& value)
{
std::vector<RatingStabilityRef>::const_iterator itInsertionPtConst
= std::lower_bound(
fRatingStabilities.begin(),
fRatingStabilities.end(),
value,
&IsRatingStabilityBefore);
std::vector<RatingStabilityRef>::iterator itInsertionPt =
fRatingStabilities.begin()
+ (itInsertionPtConst - fRatingStabilities.begin());
if (itInsertionPt != fRatingStabilities.end()
&& (*itInsertionPt)->Code() == value->Code()) {
itInsertionPt = fRatingStabilities.erase(itInsertionPt);
// replace the one with the same code.
}
fRatingStabilities.insert(itInsertionPt, value);
} }
// #pragma mark - Categories // #pragma mark - Categories
int32 bool
Model::CountCategories() const Model::HasCategories()
{ {
return fCategories.size(); BAutolock locker(&fLock);
return !fCategories.empty();
} }
CategoryRef const std::vector<CategoryRef>
Model::CategoryByCode(BString& code) const Model::Categories() const
{ {
std::vector<CategoryRef>::const_iterator it; BAutolock locker(&fLock);
for (it = fCategories.begin(); it != fCategories.end(); it++) { return fCategories;
CategoryRef aCategory = *it;
if (aCategory->Code() == code)
return aCategory;
}
return CategoryRef();
}
CategoryRef
Model::CategoryAtIndex(int32 index) const
{
return fCategories[index];
} }
void void
Model::AddCategories(std::vector<CategoryRef>& values) Model::SetCategories(const std::vector<CategoryRef> value)
{ {
std::vector<CategoryRef>::iterator it; BAutolock locker(&fLock);
for (it = values.begin(); it != values.end(); it++) fCategories = value;
_AddCategory(*it); std::sort(fCategories.begin(), fCategories.end(), IsPackageCategoryRefLess);
_NotifyCategoryListChanged(); _NotifyCategoryListChanged();
} }
/*! This will insert the category in order.
*/
void
Model::_AddCategory(const CategoryRef& category)
{
std::vector<CategoryRef>::const_iterator itInsertionPtConst
= std::lower_bound(
fCategories.begin(),
fCategories.end(),
category,
&IsPackageCategoryBefore);
std::vector<CategoryRef>::iterator itInsertionPt =
fCategories.begin() + (itInsertionPtConst - fCategories.begin());
if (itInsertionPt != fCategories.end()
&& (*itInsertionPt)->Code() == category->Code()) {
itInsertionPt = fCategories.erase(itInsertionPt);
// replace the one with the same code.
}
fCategories.insert(itInsertionPt, category);
}
void void
Model::ScreenshotCached(const ScreenshotCoordinate& coord) Model::ScreenshotCached(const ScreenshotCoordinate& coord)
{ {
BAutolock locker(&fLock);
std::vector<ModelListenerRef>::const_iterator it; std::vector<ModelListenerRef>::const_iterator it;
for (it = fListeners.begin(); it != fListeners.end(); it++) { for (it = fListeners.begin(); it != fListeners.end(); it++) {
const ModelListenerRef& listener = *it; const ModelListenerRef& listener = *it;
+82 -62
View File
@@ -1,21 +1,23 @@
/* /*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>. * Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2016-2024, Andrew Lindesay <[email protected]>. * Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef MODEL_H #ifndef MODEL_H
#define MODEL_H #define MODEL_H
#include <vector> #include <vector>
#include <map>
#include <Locker.h> #include <Locker.h>
#include "AbstractProcess.h" #include "AbstractProcess.h"
#include "DepotInfo.h" #include "DepotInfo.h"
#include "LanguageRepository.h" #include "PackageFilter.h"
#include "PackageFilterModel.h" #include "PackageFilterSpecification.h"
#include "PackageIconTarRepository.h" #include "PackageIconDefaultRepository.h"
#include "PackageInfo.h" #include "PackageInfo.h"
#include "PackageInfoListener.h"
#include "PackageScreenshotRepository.h" #include "PackageScreenshotRepository.h"
#include "RatingStability.h" #include "RatingStability.h"
#include "ScreenshotCoordinate.h" #include "ScreenshotCoordinate.h"
@@ -40,6 +42,8 @@ public:
virtual void AuthorizationChanged() = 0; virtual void AuthorizationChanged() = 0;
virtual void CategoryListChanged() = 0; virtual void CategoryListChanged() = 0;
virtual void ScreenshotCached(const ScreenshotCoordinate& coordinate) = 0; virtual void ScreenshotCached(const ScreenshotCoordinate& coordinate) = 0;
virtual void IconsChanged() = 0;
virtual void PackageFilterChanged() = 0;
}; };
@@ -59,77 +63,82 @@ public:
Model(); Model();
virtual ~Model(); virtual ~Model();
PackageFilterModel* PackageFilter(); void Clear();
PackageIconRepository&
GetPackageIconRepository(); void AddListener(const ModelListenerRef& listener);
status_t InitPackageIconRepository(); void AddPackageListener(const PackageInfoListenerRef& packageListener);
status_t DearchiveInfoEvents(const BMessage* message,
PackageInfoEvents& packageInfoEvents) const;
PackageScreenshotRepository* PackageScreenshotRepository*
GetPackageScreenshotRepository(); GetPackageScreenshotRepository();
BLocker* Lock() void SetFilterSpecification(const PackageFilterSpecificationRef& value);
{ return &fLock; } const PackageFilterSpecificationRef
FilterSpecification() const;
PackageFilterRef Filter() const;
void AddListener(const ModelListenerRef& listener); PackageIconRepositoryRef
IconRepository();
void SetIconRepository(PackageIconRepositoryRef value);
LanguageRef PreferredLanguage() const; const std::vector<PackageInfoRef>
void SetPreferredLanguage(LanguageRef value); Packages() const;
LanguageRepository* Languages(); const std::vector<PackageInfoRef>
FilteredPackages() const;
PackageInfoRef PackageForName(const BString& name); void AddPackage(const PackageInfoRef& package);
void AddPackages(const std::vector<PackageInfoRef>& packages);
void MergeOrAddDepot(const DepotInfoRef& depot); void AddPackagesWithChange(const std::vector<PackageInfoRef>& packages,
bool HasDepot(const BString& name) const; uint32 changesMask);
int32 CountDepots() const; const PackageInfoRef
DepotInfoRef DepotAtIndex(int32 index) const; PackageForName(const BString& name) const;
const DepotInfoRef DepotForName(const BString& name) const; bool HasPackage(const BString& packageName) const;
bool HasAnyProminentPackages(); bool HasAnyProminentPackages();
void Clear(); const LanguageRef PreferredLanguage() const;
void SetPreferredLanguage(const LanguageRef& value);
const std::vector<LanguageRef>
Languages() const;
void SetLanguagesAndPreferred(const std::vector<LanguageRef>& value,
const LanguageRef& preferred);
int32 CountCategories() const; void SetDepots(const DepotInfoRef& depot);
CategoryRef CategoryByCode(BString& code) const; void SetDepots(const std::vector<DepotInfoRef>& depots);
CategoryRef CategoryAtIndex(int32 index) const; const std::vector<DepotInfoRef>
void AddCategories( Depots() const;
std::vector<CategoryRef>& values); const DepotInfoRef DepotForName(const BString& name) const;
const DepotInfoRef DepotForIdentifier(const BString& identifier) const;
int32 CountRatingStabilities() const; const std::vector<CategoryRef>
RatingStabilityRef RatingStabilityByCode(BString& code) const; Categories() const;
RatingStabilityRef RatingStabilityAtIndex(int32 index) const; void SetCategories(const std::vector<CategoryRef> value);
void AddRatingStabilities( bool HasCategories();
std::vector<RatingStabilityRef>& values);
void SetStateForPackagesByName(
BStringList& packageNames,
PackageState state);
const std::vector<RatingStabilityRef>
RatingStabilities() const;
void SetRatingStabilities(const std::vector<RatingStabilityRef> value);
void SetPackageListViewMode( void SetPackageListViewMode(
package_list_view_mode mode); package_list_view_mode mode);
package_list_view_mode package_list_view_mode
PackageListViewMode() const PackageListViewMode() const;
{ return fPackageListViewMode; }
void SetCanShareAnonymousUsageData(bool value); void SetCanShareAnonymousUsageData(bool value);
bool CanShareAnonymousUsageData() const bool CanShareAnonymousUsageData() const;
{ return fCanShareAnonymousUsageData; }
bool CanPopulatePackage( bool CanPopulatePackage(const PackageInfoRef& package);
const PackageInfoRef& package);
void SetNickname(BString nickname); void SetCredentials(const UserCredentials& credentials);
const BString& Nickname(); const BString& Nickname();
void SetCredentials(const BString& nickname, WebAppInterfaceRef WebApp();
const BString& passwordClear,
bool storePassword);
WebAppInterface* GetWebAppInterface()
{ return &fWebAppInterface; }
// PackageScreenshotRepositoryListener // PackageScreenshotRepositoryListener
virtual void ScreenshotCached(const ScreenshotCoordinate& coord); virtual void ScreenshotCached(const ScreenshotCoordinate& coord);
private: private:
void _AddCategory(const CategoryRef& category); uint32 _ChangeDiff(const PackageInfoRef& package);
void _AddRatingStability( void _AddRatingStability(
const RatingStabilityRef& value); const RatingStabilityRef& value);
@@ -138,41 +147,52 @@ private:
const BMessage &responsePayload, const BMessage &responsePayload,
const char *sourceDescription) const; const char *sourceDescription) const;
void _NotifyPackageFilterChanged();
void _NotifyIconsChanged();
void _NotifyAuthorizationChanged(); void _NotifyAuthorizationChanged();
void _NotifyCategoryListChanged(); void _NotifyCategoryListChanged();
void _NotifyPackageChange(const PackageInfoEvent& event);
void _NotifyPackageChanges(const PackageInfoEvents& events);
private: private:
BLocker fLock; mutable BLocker fLock;
LanguageRef fPreferredLanguage; LanguageRef fPreferredLanguage;
std::vector<DepotInfoRef> std::map<BString, DepotInfoRef>
fDepots; fDepots;
std::map<BString, PackageInfoRef>
fPackages;
std::vector<CategoryRef> std::vector<CategoryRef>
fCategories; fCategories;
std::vector<RatingStabilityRef> std::vector<RatingStabilityRef>
fRatingStabilities; fRatingStabilities;
BStringList fPopulatedPackageNames;
package_list_view_mode package_list_view_mode
fPackageListViewMode; fPackageListViewMode;
bool fCanShareAnonymousUsageData; bool fCanShareAnonymousUsageData;
WebAppInterface fWebAppInterface; WebAppInterfaceRef fWebApp;
PackageFilterModel* fPackageFilterModel; PackageFilterSpecificationRef
fFilterSpecification;
PackageFilterRef fFilter;
LanguageRepository* fLanguageRepository; std::vector<LanguageRef>
PackageIconTarRepository fLanguages;
fPackageIconRepository; PackageIconRepositoryRef
fIconRepository;
PackageScreenshotRepository* PackageScreenshotRepository*
fPackageScreenshotRepository; fPackageScreenshotRepository;
std::vector<ModelListenerRef> std::vector<ModelListenerRef>
fListeners; fListeners;
std::vector<PackageInfoListenerRef>
fPackageListeners;
}; };
#endif // PACKAGE_INFO_H #endif // MODEL_H
@@ -1,5 +1,5 @@
/* /*
* Copyright 2013, Stephan Aßmus <[email protected]>. * Copyright 2013-2025, Stephan Aßmus <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#include "PackageInfoListener.h" #include "PackageInfoListener.h"
@@ -9,6 +9,11 @@
#include "PackageInfo.h" #include "PackageInfo.h"
const char* kPackageInfoChangesKey = "change";
const char* kPackageInfoPackageNameKey = "packageName";
const char* kPackageInfoEventsKey = "events";
// #pragma mark - PackageInfoEvent // #pragma mark - PackageInfoEvent
@@ -20,8 +25,7 @@ PackageInfoEvent::PackageInfoEvent()
} }
PackageInfoEvent::PackageInfoEvent(const PackageInfoRef& package, PackageInfoEvent::PackageInfoEvent(const PackageInfoRef& package, uint32 changes)
uint32 changes)
: :
fPackage(package), fPackage(package),
fChanges(changes) fChanges(changes)
@@ -47,9 +51,7 @@ PackageInfoEvent::operator==(const PackageInfoEvent& other)
{ {
if (this == &other) if (this == &other)
return true; return true;
return fPackage == other.fPackage && fChanges == other.fChanges;
return fPackage == other.fPackage
&& fChanges == other.fChanges;
} }
@@ -72,6 +74,85 @@ PackageInfoEvent::operator=(const PackageInfoEvent& other)
} }
status_t
PackageInfoEvent::Archive(BMessage* into, bool deep) const
{
status_t result = B_OK;
if (result == B_OK)
result = into->AddUInt32(kPackageInfoChangesKey, fChanges);
if (result == B_OK)
result = into->AddString(kPackageInfoPackageNameKey, fPackage->Name());
return result;
}
// #pragma mark - PackageInfoEvents
PackageInfoEvents::PackageInfoEvents()
{
}
PackageInfoEvents::PackageInfoEvents(const PackageInfoEvent& event)
{
fEvents.push_back(event);
}
PackageInfoEvents::PackageInfoEvents(const PackageInfoEvents& other)
{
for (int32 i = other.CountEvents() - 1; i >= 0; i--)
fEvents.push_back(other.EventAtIndex(i));
}
void
PackageInfoEvents::AddEvent(const PackageInfoEvent event)
{
fEvents.push_back(event);
}
bool
PackageInfoEvents::IsEmpty() const
{
return fEvents.empty();
}
int32
PackageInfoEvents::CountEvents() const
{
return fEvents.size();
}
const PackageInfoEvent&
PackageInfoEvents::EventAtIndex(int32 index) const
{
return fEvents[index];
}
status_t
PackageInfoEvents::Archive(BMessage* into, bool deep) const
{
status_t result = B_OK;
BString indexString;
std::vector<PackageInfoEvent>::const_iterator it;
for (it = fEvents.begin(); it != fEvents.end(); it++) {
BMessage eventMessage;
result = (*it).Archive(&eventMessage);
if (result == B_OK)
result = into->AddMessage(kPackageInfoEventsKey, &eventMessage);
}
return result;
}
// #pragma mark - PackageInfoListener // #pragma mark - PackageInfoListener
+39 -12
View File
@@ -1,15 +1,24 @@
/* /*
* Copyright 2013, Stephan Aßmus <[email protected]>. * Copyright 2013, Stephan Aßmus <[email protected]>.
* Copyright 2022, Andrew Lindesay <[email protected]>. * Copyright 2022-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef PACKAGE_INFO_LISTENER_H #ifndef PACKAGE_INFO_LISTENER_H
#define PACKAGE_INFO_LISTENER_H #define PACKAGE_INFO_LISTENER_H
#include <vector>
#include <Archivable.h>
#include <String.h>
#include <Referenceable.h> #include <Referenceable.h>
extern const char* kPackageInfoChangesKey;
extern const char* kPackageInfoPackageNameKey;
extern const char* kPackageInfoEventsKey;
enum { enum {
PKG_CHANGED_LOCALIZED_TEXT = 1 << 0, PKG_CHANGED_LOCALIZED_TEXT = 1 << 0,
// ^ Covers title, summary, description and changelog. // ^ Covers title, summary, description and changelog.
@@ -17,13 +26,10 @@ enum {
PKG_CHANGED_SCREENSHOTS = 1 << 2, PKG_CHANGED_SCREENSHOTS = 1 << 2,
PKG_CHANGED_LOCAL_INFO = 1 << 3, PKG_CHANGED_LOCAL_INFO = 1 << 3,
// ^ Covers state, download and size. // ^ Covers state, download and size.
PKG_CHANGED_ICON = 1 << 4, PKG_CHANGED_CLASSIFICATION = 1 << 4,
// ^ Handled slightly differently.
PKG_CHANGED_CLASSIFICATION = 1 << 5,
// ^ This covers categories, prominence and is native desktop // ^ This covers categories, prominence and is native desktop
PKG_CHANGED_CORE_INFO = 1 << 6 PKG_CHANGED_CORE_INFO = 1 << 5
// ^ covers the version change timestamp // ^ covers the version change timestamp
// ...
}; };
@@ -31,11 +37,10 @@ class PackageInfo;
typedef BReference<PackageInfo> PackageInfoRef; typedef BReference<PackageInfo> PackageInfoRef;
class PackageInfoEvent { class PackageInfoEvent : public BArchivable {
public: public:
PackageInfoEvent(); PackageInfoEvent();
PackageInfoEvent(const PackageInfoRef& package, PackageInfoEvent(const PackageInfoRef& package, uint32 changes);
uint32 changes);
PackageInfoEvent(const PackageInfoEvent& other); PackageInfoEvent(const PackageInfoEvent& other);
virtual ~PackageInfoEvent(); virtual ~PackageInfoEvent();
@@ -43,25 +48,47 @@ public:
bool operator!=(const PackageInfoEvent& other); bool operator!=(const PackageInfoEvent& other);
PackageInfoEvent& operator=(const PackageInfoEvent& other); PackageInfoEvent& operator=(const PackageInfoEvent& other);
inline const PackageInfoRef& Package() const inline const PackageInfoRef&
Package() const
{ return fPackage; } { return fPackage; }
inline uint32 Changes() const inline uint32 Changes() const
{ return fChanges; } { return fChanges; }
status_t Archive(BMessage* into, bool deep = true) const;
private: private:
PackageInfoRef fPackage; PackageInfoRef fPackage;
uint32 fChanges; uint32 fChanges;
}; };
class PackageInfoEvents : public BArchivable {
public:
PackageInfoEvents();
PackageInfoEvents(const PackageInfoEvent& event);
PackageInfoEvents(const PackageInfoEvents& other);
bool IsEmpty() const;
void AddEvent(const PackageInfoEvent event);
int32 CountEvents() const;
const PackageInfoEvent&
EventAtIndex(int32 index) const;
status_t Archive(BMessage* into, bool deep = true) const;
private:
std::vector<PackageInfoEvent>
fEvents;
};
class PackageInfoListener : public BReferenceable { class PackageInfoListener : public BReferenceable {
public: public:
PackageInfoListener(); PackageInfoListener();
virtual ~PackageInfoListener(); virtual ~PackageInfoListener();
virtual void PackageChanged( virtual void PackagesChanged(const PackageInfoEvents& events) = 0;
const PackageInfoEvent& event) = 0;
}; };
@@ -1,5 +1,5 @@
/* /*
* Copyright 2024, Andrew Lindesay <[email protected]>. * Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#include "PackageScreenshotRepository.h" #include "PackageScreenshotRepository.h"
@@ -11,6 +11,7 @@
#include "FileIO.h" #include "FileIO.h"
#include "Logger.h" #include "Logger.h"
#include "Model.h"
#include "StorageUtils.h" #include "StorageUtils.h"
#include "WebAppInterface.h" #include "WebAppInterface.h"
@@ -19,11 +20,10 @@ static const uint32 kMaxRetainedCachedScreenshots = 25;
PackageScreenshotRepository::PackageScreenshotRepository( PackageScreenshotRepository::PackageScreenshotRepository(
PackageScreenshotRepositoryListenerRef listener, PackageScreenshotRepositoryListenerRef listener, Model* model)
WebAppInterface* webAppInterface)
: :
fListener(listener), fListener(listener),
fWebAppInterface(webAppInterface) fModel(model)
{ {
_Init(); _Init();
} }
@@ -162,8 +162,7 @@ PackageScreenshotRepository::_Init()
{ {
fBaseDirectory.Unset(); fBaseDirectory.Unset();
status_t result = StorageUtils::LocalWorkingDirectoryPath( status_t result = StorageUtils::LocalWorkingDirectoryPath("screenshot_cache", fBaseDirectory);
"screenshot_cache", fBaseDirectory);
if (B_OK != result) if (B_OK != result)
HDERROR("unable to setup the cache directory"); HDERROR("unable to setup the cache directory");
@@ -202,8 +201,8 @@ PackageScreenshotRepository::_DownloadToLocalFile(const ScreenshotCoordinate& co
} }
BFileIO outputDataStream(file, true); // takes ownership BFileIO outputDataStream(file, true); // takes ownership
status_t result = fWebAppInterface->RetrieveScreenshot( status_t result = _WebApp()->RetrieveScreenshot(coord.Code(), coord.Width(), coord.Height(),
coord.Code(), coord.Width(), coord.Height(), &outputDataStream); &outputDataStream);
if (result == B_OK) if (result == B_OK)
result = outputDataStream.Flush(); result = outputDataStream.Flush();
@@ -225,7 +224,8 @@ PackageScreenshotRepository::_DeriveCachePath(const ScreenshotCoordinate& coord)
status_t status_t
PackageScreenshotRepository::_CreateCachedData(const ScreenshotCoordinate& coord, BPositionIO** data) PackageScreenshotRepository::_CreateCachedData(const ScreenshotCoordinate& coord,
BPositionIO** data)
{ {
status_t result = B_OK; status_t result = B_OK;
BPath path = _DeriveCachePath(coord); BPath path = _DeriveCachePath(coord);
@@ -243,3 +243,10 @@ PackageScreenshotRepository::_CreateCachedData(const ScreenshotCoordinate& coord
return result; return result;
} }
WebAppInterfaceRef
PackageScreenshotRepository::_WebApp()
{
return fModel->WebApp();
}
@@ -1,5 +1,5 @@
/* /*
* Copyright 2024, Andrew Lindesay <[email protected]>. * Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef SCREENSHOT_REPOSITORY_H #ifndef SCREENSHOT_REPOSITORY_H
@@ -12,9 +12,10 @@
#include "BitmapHolder.h" #include "BitmapHolder.h"
#include "ScreenshotCoordinate.h" #include "ScreenshotCoordinate.h"
#include "WebAppInterface.h"
class WebAppInterface; class Model;
class PackageScreenshotRepositoryListener : public BReferenceable { class PackageScreenshotRepositoryListener : public BReferenceable {
@@ -36,7 +37,7 @@ public:
PackageScreenshotRepository( PackageScreenshotRepository(
PackageScreenshotRepositoryListenerRef listener, PackageScreenshotRepositoryListenerRef listener,
WebAppInterface* webAppInterface); Model* model);
~PackageScreenshotRepository(); ~PackageScreenshotRepository();
status_t LoadScreenshot(const ScreenshotCoordinate& coord, status_t LoadScreenshot(const ScreenshotCoordinate& coord,
@@ -48,6 +49,8 @@ public:
status_t CacheScreenshot(const ScreenshotCoordinate& coord); status_t CacheScreenshot(const ScreenshotCoordinate& coord);
private: private:
WebAppInterfaceRef _WebApp();
status_t _Init(); status_t _Init();
status_t _CleanCache(); status_t _CleanCache();
@@ -61,7 +64,7 @@ private:
private: private:
PackageScreenshotRepositoryListenerRef PackageScreenshotRepositoryListenerRef
fListener; fListener;
WebAppInterface* fWebAppInterface; Model* fModel;
BPath fBaseDirectory; BPath fBaseDirectory;
}; };
+10 -15
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 2020, Andrew Lindesay <[email protected]>. * Copyright 2020-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
@@ -12,12 +12,12 @@
#include "Logger.h" #include "Logger.h"
bool IsRatingStabilityBefore(const RatingStabilityRef& rs1, bool
const RatingStabilityRef& rs2) IsRatingStabilityRefLess(const RatingStabilityRef& rs1, const RatingStabilityRef& rs2)
{ {
if (!rs1.IsSet() || !rs2.IsSet()) if (!rs1.IsSet() || !rs2.IsSet())
HDFATAL("unexpected NULL reference in a referencable"); debugger("illegal state in rating stability less");
return rs1->Compare(*rs2) < 0; return *(rs1.Get()) < *(rs2.Get());
} }
@@ -30,8 +30,7 @@ RatingStability::RatingStability()
} }
RatingStability::RatingStability(const BString& code, RatingStability::RatingStability(const BString& code, const BString& name, int64 ordering)
const BString& name, int64 ordering)
: :
fCode(code), fCode(code),
fName(name), fName(name),
@@ -49,21 +48,17 @@ RatingStability::RatingStability(const RatingStability& other)
} }
RatingStability& bool
RatingStability::operator=(const RatingStability& other) RatingStability::operator<(const RatingStability& other) const
{ {
fCode = other.fCode; return Compare(other) < 0;
fName = other.fName;
fOrdering = other.fOrdering;
return *this;
} }
bool bool
RatingStability::operator==(const RatingStability& other) const RatingStability::operator==(const RatingStability& other) const
{ {
return fCode == other.fCode && fName == other.fName return fCode == other.fCode && fName == other.fName && fOrdering == other.fOrdering;
&& fOrdering == other.fOrdering;
} }
+5 -8
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 2020, Andrew Lindesay <[email protected]>. * Copyright 2020-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef RATING_STABILITY_H #ifndef RATING_STABILITY_H
@@ -20,12 +20,9 @@ public:
RatingStability( RatingStability(
const RatingStability& other); const RatingStability& other);
RatingStability& bool operator<(const RatingStability& other) const;
operator=(const RatingStability& other); bool operator==(const RatingStability& other) const;
bool operator==(const RatingStability& other) bool operator!=(const RatingStability& other) const;
const;
bool operator!=(const RatingStability& other)
const;
const BString& Code() const const BString& Code() const
{ return fCode; } { return fCode; }
@@ -46,7 +43,7 @@ private:
typedef BReference<RatingStability> RatingStabilityRef; typedef BReference<RatingStability> RatingStabilityRef;
extern bool IsRatingStabilityBefore(const RatingStabilityRef& rs1, extern bool IsRatingStabilityRefLess(const RatingStabilityRef& rs1,
const RatingStabilityRef& rs2); const RatingStabilityRef& rs2);
@@ -22,8 +22,7 @@ UserCredentials::UserCredentials(BMessage* from)
} }
UserCredentials::UserCredentials(const BString& nickname, UserCredentials::UserCredentials(const BString& nickname, const BString& passwordClear)
const BString& passwordClear)
: :
fNickname(nickname), fNickname(nickname),
fPasswordClear(passwordClear), fPasswordClear(passwordClear),
@@ -140,4 +139,4 @@ UserCredentials::Archive(BMessage* into, bool deep) const
if (result == B_OK) if (result == B_OK)
result = into->AddBool(KEY_IS_SUCCESSFUL, fIsSuccessful); result = into->AddBool(KEY_IS_SUCCESSFUL, fIsSuccessful);
return result; return result;
} }
+1 -1
View File
@@ -18,11 +18,11 @@
class UserCredentials : public BArchivable { class UserCredentials : public BArchivable {
public: public:
UserCredentials();
UserCredentials(BMessage* from); UserCredentials(BMessage* from);
UserCredentials(const BString& nickname, UserCredentials(const BString& nickname,
const BString& passwordClear); const BString& passwordClear);
UserCredentials(const UserCredentials& other); UserCredentials(const UserCredentials& other);
UserCredentials();
virtual ~UserCredentials(); virtual ~UserCredentials();
UserCredentials& operator=(const UserCredentials& other); UserCredentials& operator=(const UserCredentials& other);
@@ -1,7 +1,7 @@
/* /*
* Copyright 2013, Stephan Aßmus <[email protected]>. * Copyright 2013, Stephan Aßmus <[email protected]>.
* Copyright 2013, Rene Gollent, <[email protected]> * Copyright 2013, Rene Gollent, <[email protected]>
* Copyright 2020-2024, Andrew Lindesay <[email protected]> * Copyright 2020-2025, Andrew Lindesay <[email protected]>
* *
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
@@ -9,6 +9,8 @@
#include "AbstractPackageProcess.h" #include "AbstractPackageProcess.h"
#include <Autolock.h>
#include "Logger.h" #include "Logger.h"
#include "Model.h" #include "Model.h"
#include "PackageKitUtils.h" #include "PackageKitUtils.h"
@@ -18,78 +20,93 @@
using namespace BPackageKit; using namespace BPackageKit;
// #pragma mark - PackageAction
AbstractPackageProcess::AbstractPackageProcess(const BString& packageName, Model* model)
AbstractPackageProcess::AbstractPackageProcess(
PackageInfoRef package, Model* model)
: :
fPackage(package), fPackageName(packageName),
fModel(model) fModel(model)
{ {
if (package.IsSet())
fInstallLocation = PackageKitUtils::DeriveInstallLocation(package.Get());
else
fInstallLocation = B_PACKAGE_INSTALLATION_LOCATION_SYSTEM;
// TODO: ideally if the package is installed at multiple locations,
// the user should be able to pick which one to remove.
// TODO: allow configuring the installation location
fPackageManager = new(std::nothrow) PackageManager(
(BPackageInstallationLocation)fInstallLocation);
} }
AbstractPackageProcess::~AbstractPackageProcess() AbstractPackageProcess::~AbstractPackageProcess()
{ {
delete fPackageManager; }
int32
AbstractPackageProcess::InstallLocation() const
{
PackageInfoRef package = FindPackageByName(fPackageName);
if (package.IsSet())
return PackageKitUtils::DeriveInstallLocation(package.Get());
return B_PACKAGE_INSTALLATION_LOCATION_SYSTEM;
} }
PackageInfoRef PackageInfoRef
AbstractPackageProcess::FindPackageByName(const BString& name) AbstractPackageProcess::FindPackageByName(const BString& packageName) const
{ {
return fModel->PackageForName(name); return fModel->PackageForName(packageName);
} }
// TODO; will refactor once the models go immutable.
void void
AbstractPackageProcess::SetPackageState(PackageInfoRef& package, PackageState state) AbstractPackageProcess::SetPackageState(const BString& packageName, PackageState state)
{ {
PackageInfoRef package = fModel->PackageForName(packageName);
if (package.IsSet()) { if (package.IsSet()) {
PackageLocalInfoRef localInfo = PackageUtils::NewLocalInfo(package); PackageLocalInfoRef localInfo
localInfo->SetState(state); = PackageLocalInfoBuilder(package->LocalInfo()).WithState(state).BuildRef();
package->SetLocalInfo(localInfo);
PackageInfoRef updatedPackage
= PackageInfoBuilder(package).WithLocalInfo(localInfo).BuildRef();
fModel->AddPackage(updatedPackage);
} else { } else {
HDERROR("setting state, but the package is not set"); HDERROR("setting state, but the package [%s] is not present", packageName.String());
} }
} }
// TODO; will refactor once the models go immutable.
void void
AbstractPackageProcess::SetPackageDownloadProgress(PackageInfoRef& package, float value) AbstractPackageProcess::SetPackageDownloadProgress(const BString& packageName, float value)
{ {
PackageInfoRef package = fModel->PackageForName(packageName);
if (package.IsSet()) { if (package.IsSet()) {
PackageLocalInfoRef localInfo = PackageUtils::NewLocalInfo(package); PackageLocalInfoRef localInfo
localInfo->SetDownloadProgress(value); = PackageLocalInfoBuilder(package->LocalInfo()).WithDownloadProgress(value).BuildRef();
package->SetLocalInfo(localInfo);
PackageInfoRef updatedPackage
= PackageInfoBuilder(package).WithLocalInfo(localInfo).BuildRef();
fModel->AddPackage(updatedPackage);
} else { } else {
HDERROR("setting progress, but the package is not set"); HDERROR("setting progress, but the package [%s] is not present", packageName.String());
} }
} }
// TODO; will refactor once the models go immutable.
void void
AbstractPackageProcess::ClearPackageInstallationLocations(PackageInfoRef& package) AbstractPackageProcess::ClearPackageInstallationLocations(const BString& packageName)
{ {
PackageInfoRef package = fModel->PackageForName(packageName);
if (package.IsSet()) { if (package.IsSet()) {
PackageLocalInfoRef localInfo = PackageUtils::NewLocalInfo(package); PackageLocalInfoRef localInfo
localInfo->ClearInstallationLocations(); = PackageLocalInfoBuilder(package->LocalInfo()).ClearInstallationLocations().BuildRef();
package->SetLocalInfo(localInfo);
PackageInfoRef updatedPackage
= PackageInfoBuilder(package).WithLocalInfo(localInfo).BuildRef();
fModel->AddPackage(updatedPackage);
} else { } else {
HDERROR("clearing installation locations, but the package is not set"); HDERROR("clearing installation locations, but the package [%s] is not present",
packageName.String());
} }
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright 2022, Andrew Lindesay <[email protected]> * Copyright 2022-2025, Andrew Lindesay <[email protected]>
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef ABSTRACT_PACKAGE_PROCESS_H #ifndef ABSTRACT_PACKAGE_PROCESS_H
@@ -18,27 +18,23 @@ class Model;
class AbstractPackageProcess : public BReferenceable, public AbstractProcess { class AbstractPackageProcess : public BReferenceable, public AbstractProcess {
public: public:
AbstractPackageProcess(PackageInfoRef package, AbstractPackageProcess(const BString& packageName, Model* model);
Model* model);
virtual ~AbstractPackageProcess(); virtual ~AbstractPackageProcess();
int32 InstallLocation() const
{ return fInstallLocation; }
protected: protected:
PackageInfoRef FindPackageByName(const BString& name); int32 InstallLocation() const;
PackageInfoRef FindPackageByName(const BString& packageName) const;
void SetPackageState(PackageInfoRef& package, PackageState state); void SetPackageState(const BString& packageName, PackageState state);
void SetPackageDownloadProgress(PackageInfoRef& package, float value); void SetPackageDownloadProgress(const BString& packageName, float value);
void ClearPackageInstallationLocations(PackageInfoRef& package); void ClearPackageInstallationLocations(const BString& packageName);
protected: protected:
PackageManager* fPackageManager; BString fPackageName;
PackageInfoRef fPackage;
private: private:
Model* fModel; Model* fModel;
int32 fInstallLocation;
}; };
@@ -1,5 +1,5 @@
/* /*
* Copyright 2013-2024, Haiku, Inc. All Rights Reserved. * Copyright 2013-2025, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
* *
* Authors: * Authors:
@@ -18,16 +18,17 @@
#include <algorithm> #include <algorithm>
#include <AutoDeleter.h>
#include <AutoLocker.h> #include <AutoLocker.h>
#include <Catalog.h> #include <Catalog.h>
#include <Locker.h> #include <Locker.h>
#include <package/manager/Exceptions.h>
#include <package/hpkg/NoErrorOutput.h> #include <package/hpkg/NoErrorOutput.h>
#include <package/hpkg/PackageContentHandler.h> #include <package/hpkg/PackageContentHandler.h>
#include <package/hpkg/PackageEntry.h> #include <package/hpkg/PackageEntry.h>
#include <package/hpkg/PackageEntryAttribute.h> #include <package/hpkg/PackageEntryAttribute.h>
#include <package/hpkg/PackageReader.h> #include <package/hpkg/PackageReader.h>
#include <package/manager/Exceptions.h>
#include <package/solver/SolverPackage.h> #include <package/solver/SolverPackage.h>
#include "AppUtils.h" #include "AppUtils.h"
@@ -57,6 +58,13 @@ using BPackageKit::BHPKG::BPackageReader;
class DownloadProgress { class DownloadProgress {
public: public:
DownloadProgress()
:
fPackageName(),
fProgress(0)
{
}
DownloadProgress(BString packageName, float progress) DownloadProgress(BString packageName, float progress)
: :
fPackageName(packageName), fPackageName(packageName),
@@ -84,14 +92,13 @@ private:
}; };
InstallPackageProcess::InstallPackageProcess( InstallPackageProcess::InstallPackageProcess(const BString& packageName, Model* model)
PackageInfoRef package, Model* model)
: :
AbstractPackageProcess(package, model), AbstractPackageProcess(packageName, model),
fLastDownloadUpdate(0) fLastDownloadUpdate(0)
{ {
fDescription = B_TRANSLATE("Installing \"%PackageName%\""); fDescription = B_TRANSLATE("Installing \"%PackageName%\"");
fDescription.ReplaceAll("%PackageName%", package->Name()); fDescription.ReplaceAll("%PackageName%", packageName);
} }
@@ -120,14 +127,20 @@ InstallPackageProcess::Progress()
if (ProcessState() == PROCESS_RUNNING && !fDownloadProgresses.empty()) { if (ProcessState() == PROCESS_RUNNING && !fDownloadProgresses.empty()) {
AutoLocker<BLocker> locker(&fLock); AutoLocker<BLocker> locker(&fLock);
float sum = 0.0; float sum = 0.0;
std::vector<DownloadProgress>::const_iterator it; int32 count = 0;
for (it = fDownloadProgresses.begin();
it != fDownloadProgresses.end(); it++) { std::map<BString, DownloadProgress>::const_iterator it;
DownloadProgress downloadProgress = *it;
sum += downloadProgress.Progress(); for (it = fDownloadProgresses.begin(); it != fDownloadProgresses.end(); it++) {
DownloadProgress downloadProgress = it->second;
if (!downloadProgress.PackageName().IsEmpty()) {
sum += downloadProgress.Progress();
count++;
}
} }
if (sum > 0.0) if (sum > 0.0)
return sum / (float) fDownloadProgresses.size(); return sum / static_cast<float>(count);
} }
return kProgressIndeterminate; return kProgressIndeterminate;
} }
@@ -136,57 +149,61 @@ InstallPackageProcess::Progress()
status_t status_t
InstallPackageProcess::RunInternal() InstallPackageProcess::RunInternal()
{ {
PackageInfoRef ref(fPackage); // TODO: allow configuring the installation location
SetPackageState(ref, PENDING);
fPackageManager->Init(BPackageManager::B_ADD_INSTALLED_REPOSITORIES PackageManager* packageManager = new(std::nothrow)
| BPackageManager::B_ADD_REMOTE_REPOSITORIES PackageManager(static_cast<BPackageInstallationLocation>(InstallLocation()));
| BPackageManager::B_REFRESH_REPOSITORIES); ObjectDeleter<PackageManager> solverDeleter(packageManager);
PackageState state = PackageUtils::State(ref); SetPackageState(fPackageName, PENDING);
fPackageManager->SetCurrentActionPackage(ref, true); packageManager->Init(BPackageManager::B_ADD_INSTALLED_REPOSITORIES
fPackageManager->AddProgressListener(this); | BPackageManager::B_ADD_REMOTE_REPOSITORIES | BPackageManager::B_REFRESH_REPOSITORIES);
BString packageName = ref->Name();
PackageLocalInfoRef localInfo = ref->LocalInfo(); PackageInfoRef package = FindPackageByName(fPackageName);
PackageState state = PackageUtils::State(package);
packageManager->SetCurrentActionPackage(package, true);
packageManager->AddProgressListener(this);
BString packageName = fPackageName;
PackageLocalInfoRef localInfo = package->LocalInfo();
if (localInfo.IsSet() && localInfo->IsLocalFile()) if (localInfo.IsSet() && localInfo->IsLocalFile())
packageName = localInfo->LocalFilePath(); packageName = localInfo->LocalFilePath();
const char* packageNameString = packageName.String(); const char* packageNameString = packageName.String();
try { try {
fPackageManager->Install(&packageNameString, 1); packageManager->Install(&packageNameString, 1);
} catch (BFatalErrorException& ex) { } catch (BFatalErrorException& ex) {
BString errorString; BString errorString;
errorString.SetToFormat( errorString.SetToFormat("Fatal error occurred while installing package %s: %s (%s)\n",
"Fatal error occurred while installing package %s: " packageNameString, ex.Message().String(), ex.Details().String());
"%s (%s)\n", packageNameString, ex.Message().String(), AppUtils::NotifySimpleError(B_TRANSLATE("Fatal error"), errorString, B_STOP_ALERT);
ex.Details().String());
AppUtils::NotifySimpleError(B_TRANSLATE("Fatal error"), errorString,
B_STOP_ALERT);
_SetDownloadedPackagesState(NONE); _SetDownloadedPackagesState(NONE);
SetPackageState(ref, state); SetPackageState(fPackageName, state);
return ex.Error(); return ex.Error();
} catch (BAbortedByUserException& ex) { } catch (BAbortedByUserException& ex) {
HDINFO("Installation of package %s is aborted by user: %s", HDINFO("Installation of package %s is aborted by user: %s", packageNameString,
packageNameString, ex.Message().String()); ex.Message().String());
_SetDownloadedPackagesState(NONE); _SetDownloadedPackagesState(NONE);
SetPackageState(ref, state); SetPackageState(fPackageName, state);
return B_OK; return B_OK;
} catch (BNothingToDoException& ex) { } catch (BNothingToDoException& ex) {
HDINFO("Nothing to do while installing package %s: %s", HDINFO("Nothing to do while installing package %s: %s", packageNameString,
packageNameString, ex.Message().String()); ex.Message().String());
return B_OK; return B_OK;
} catch (BException& ex) { } catch (BException& ex) {
HDERROR("Exception occurred while installing package %s: %s", HDERROR("Exception occurred while installing package %s: %s", packageNameString,
packageNameString, ex.Message().String()); ex.Message().String());
_SetDownloadedPackagesState(NONE); _SetDownloadedPackagesState(NONE);
SetPackageState(ref, state); SetPackageState(fPackageName, state);
return B_ERROR; return B_ERROR;
} }
fPackageManager->RemoveProgressListener(this); packageManager->RemoveProgressListener(this);
_SetDownloadedPackagesState(ACTIVATED); _SetDownloadedPackagesState(ACTIVATED);
@@ -198,26 +215,20 @@ InstallPackageProcess::RunInternal()
void void
InstallPackageProcess::DownloadProgressChanged( InstallPackageProcess::DownloadProgressChanged(const char* packageName, float progress)
const char* packageName, float progress)
{ {
bigtime_t now = system_time(); if (!_ShouldProcessProgress() && progress != 1.0)
if (now - fLastDownloadUpdate < 250000 && progress != 1.0)
return; return;
fLastDownloadUpdate = now;
BString simplePackageName; BString simplePackageName;
if (_DeriveSimplePackageName(packageName, simplePackageName) != B_OK) { if (_DeriveSimplePackageName(packageName, simplePackageName) != B_OK) {
HDERROR("malformed canonical package name [%s]", packageName); HDERROR("malformed canonical package name [%s]", packageName);
return; return;
} }
PackageInfoRef ref(FindPackageByName(simplePackageName));
if (ref.IsSet()) { SetPackageDownloadProgress(simplePackageName, progress);
SetPackageDownloadProgress(ref, progress); _SetDownloadProgress(simplePackageName, progress);
_SetDownloadProgress(simplePackageName, progress);
} else {
HDERROR("unable to find the package info for simple package name [%s]",
simplePackageName.String());
}
} }
@@ -230,29 +241,21 @@ InstallPackageProcess::DownloadProgressComplete(const char* packageName)
return; return;
} }
_SetDownloadProgress(simplePackageName, 1.0); _SetDownloadProgress(simplePackageName, 1.0);
PackageInfoRef ref(FindPackageByName(simplePackageName)); SetPackageDownloadProgress(simplePackageName, 1.0);
if (!ref.IsSet()) { fDownloadedPackageNames.insert(simplePackageName);
HDERROR("unable to find the package info for simple package name [%s]",
simplePackageName.String());
return;
}
SetPackageDownloadProgress(ref, 1.0);
fDownloadedPackages.insert(ref);
} }
void void
InstallPackageProcess::ConfirmedChanges( InstallPackageProcess::ConfirmedChanges(BPackageManager::InstalledRepository& repository)
BPackageManager::InstalledRepository& repository)
{ {
BPackageManager::InstalledRepository::PackageList& activationList = BPackageManager::InstalledRepository::PackageList& activationList
repository.PackagesToActivate(); = repository.PackagesToActivate();
BSolverPackage* package = NULL; BSolverPackage* package = NULL;
for (int32 i = 0; (package = activationList.ItemAt(i)); i++) { for (int32 i = 0; (package = activationList.ItemAt(i)); i++) {
PackageInfoRef ref(FindPackageByName(package->Info().Name())); BString packageName = package->Info().Name();
if (ref.IsSet()) SetPackageState(packageName, PENDING);
SetPackageState(ref, PENDING);
} }
} }
@@ -260,22 +263,14 @@ InstallPackageProcess::ConfirmedChanges(
void void
InstallPackageProcess::_SetDownloadedPackagesState(PackageState state) InstallPackageProcess::_SetDownloadedPackagesState(PackageState state)
{ {
for (PackageInfoSet::iterator it = fDownloadedPackages.begin(); std::set<BString>::const_iterator it;
it != fDownloadedPackages.end(); ++it) { for (it = fDownloadedPackageNames.begin(); it != fDownloadedPackageNames.end(); ++it) {
PackageInfoRef ref = (*it); BString packageName = *it;
SetPackageState(ref, state); SetPackageState(packageName, state);
} }
} }
static bool
_IsDownloadProgressBefore(const DownloadProgress& dp1,
const DownloadProgress& dp2)
{
return dp1.PackageName().Compare(dp2.PackageName()) < 0;
}
/*! This method will extract the plain package name from the canonical /*! This method will extract the plain package name from the canonical
*/ */
@@ -293,22 +288,9 @@ InstallPackageProcess::_DeriveSimplePackageName(const BString& canonicalForm,
void void
InstallPackageProcess::_SetDownloadProgress(const BString& simplePackageName, InstallPackageProcess::_SetDownloadProgress(const BString& simplePackageName, float progress)
float progress)
{ {
AutoLocker<BLocker> locker(&fLock);
DownloadProgress downloadProgress(simplePackageName, progress); DownloadProgress downloadProgress(simplePackageName, progress);
std::vector<DownloadProgress>::iterator itInsertionPt fDownloadProgresses[simplePackageName] = downloadProgress;
= std::lower_bound(
fDownloadProgresses.begin(), fDownloadProgresses.end(),
downloadProgress, &_IsDownloadProgressBefore);
if (itInsertionPt != fDownloadProgresses.end()) {
if ((*itInsertionPt).PackageName() == simplePackageName) {
itInsertionPt = fDownloadProgresses.erase(itInsertionPt);
}
}
fDownloadProgresses.insert(itInsertionPt, downloadProgress);
_NotifyChanged(); _NotifyChanged();
} }
@@ -3,7 +3,7 @@
* Copyright 2011, Ingo Weinhold, <[email protected]> * Copyright 2011, Ingo Weinhold, <[email protected]>
* Copyright 2013, Rene Gollent, <[email protected]> * Copyright 2013, Rene Gollent, <[email protected]>
* Copyright 2017, Julian Harnath <[email protected]>. * Copyright 2017, Julian Harnath <[email protected]>.
* Copyright 2021, Andrew Lindesay <[email protected]>. * Copyright 2021-2025, Andrew Lindesay <[email protected]>.
* *
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
* *
@@ -30,7 +30,7 @@ class InstallPackageProcess
private PackageProgressListener { private PackageProgressListener {
public: public:
InstallPackageProcess( InstallPackageProcess(
PackageInfoRef package, Model* model); const BString& packageName, Model* model);
virtual ~InstallPackageProcess(); virtual ~InstallPackageProcess();
virtual const char* Name() const; virtual const char* Name() const;
@@ -63,11 +63,11 @@ private:
float progress); float progress);
private: private:
std::vector<DownloadProgress> std::map<BString, DownloadProgress>
fDownloadProgresses; fDownloadProgresses;
BString fDescription; BString fDescription;
bigtime_t fLastDownloadUpdate; bigtime_t fLastDownloadUpdate;
PackageInfoSet fDownloadedPackages; std::set<BString> fDownloadedPackageNames;
}; };
#endif // INSTALL_PACKAGE_PROCESS_H #endif // INSTALL_PACKAGE_PROCESS_H
@@ -1,5 +1,5 @@
/* /*
* Copyright 2013-2022, Haiku, Inc. All Rights Reserved. * Copyright 2013-2025, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
* *
* Authors: * Authors:
@@ -64,8 +64,7 @@ public:
return B_OK; return B_OK;
} }
virtual status_t HandleEntryAttribute(BPackageEntry* entry, virtual status_t HandleEntryAttribute(BPackageEntry* entry, BPackageEntryAttribute* attribute)
BPackageEntryAttribute* attribute)
{ {
return B_OK; return B_OK;
} }
@@ -75,8 +74,7 @@ public:
return B_OK; return B_OK;
} }
virtual status_t HandlePackageAttribute( virtual status_t HandlePackageAttribute(const BPackageInfoAttributeValue& value)
const BPackageInfoAttributeValue& value)
{ {
return B_OK; return B_OK;
} }
@@ -105,10 +103,10 @@ private:
// #pragma mark - OpenPackageProcess // #pragma mark - OpenPackageProcess
OpenPackageProcess::OpenPackageProcess(PackageInfoRef package, Model* model, OpenPackageProcess::OpenPackageProcess(const BString& packageName, Model* model,
const DeskbarLink& link) const DeskbarLink& link)
: :
AbstractPackageProcess(package, model), AbstractPackageProcess(packageName, model),
fDeskbarLink(link) fDeskbarLink(link)
{ {
fDescription = _DeriveDescription(); fDescription = _DeriveDescription();
@@ -192,7 +190,7 @@ OpenPackageProcess::_DeriveDescription()
/*static*/ bool /*static*/ bool
OpenPackageProcess::FindAppToLaunch(const PackageInfoRef& package, OpenPackageProcess::FindAppToLaunch(const PackageInfoRef& package,
std::vector<DeskbarLink>& foundLinks) std::vector<DeskbarLink>& foundLinks)
{ {
if (!package.IsSet()) if (!package.IsSet())
return false; return false;
@@ -208,8 +206,7 @@ OpenPackageProcess::FindAppToLaunch(const PackageInfoRef& package,
status_t status = reader.Init(packagePath.Path()); status_t status = reader.Init(packagePath.Path());
if (status != B_OK) { if (status != B_OK) {
HDINFO("OpenPackageAction::FindAppToLaunch(): " HDINFO("OpenPackageAction::FindAppToLaunch(): failed to init BPackageReader(%s): %s",
"failed to init BPackageReader(%s): %s",
packagePath.Path(), strerror(status)); packagePath.Path(), strerror(status));
return false; return false;
} }
@@ -218,8 +215,7 @@ OpenPackageProcess::FindAppToLaunch(const PackageInfoRef& package,
DeskbarLinkFinder contentHandler(foundLinks); DeskbarLinkFinder contentHandler(foundLinks);
status = reader.ParseContent(&contentHandler); status = reader.ParseContent(&contentHandler);
if (status != B_OK) { if (status != B_OK) {
HDINFO("OpenPackageAction::FindAppToLaunch(): " HDINFO("OpenPackageAction::FindAppToLaunch(): failed parse package contents (%s): %s",
"failed parse package contents (%s): %s",
packagePath.Path(), strerror(status)); packagePath.Path(), strerror(status));
return false; return false;
} }
@@ -3,7 +3,7 @@
* Copyright 2011, Ingo Weinhold, <[email protected]> * Copyright 2011, Ingo Weinhold, <[email protected]>
* Copyright 2013, Rene Gollent, <[email protected]> * Copyright 2013, Rene Gollent, <[email protected]>
* Copyright 2017, Julian Harnath <[email protected]> * Copyright 2017, Julian Harnath <[email protected]>
* Copyright 2021, Andrew Lindesay <[email protected]> * Copyright 2021-2025, Andrew Lindesay <[email protected]>
* *
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
* *
@@ -24,7 +24,7 @@ class OpenPackageProcess
private PackageProgressListener { private PackageProgressListener {
public: public:
OpenPackageProcess( OpenPackageProcess(
PackageInfoRef package, Model* model, const BString& packageName, Model* model,
const DeskbarLink& link); const DeskbarLink& link);
virtual ~OpenPackageProcess(); virtual ~OpenPackageProcess();
@@ -1,5 +1,5 @@
/* /*
* Copyright 2013-2021, Haiku, Inc. All Rights Reserved. * Copyright 2013-2025, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
* *
* Authors: * Authors:
@@ -19,6 +19,7 @@
#include <package/manager/Exceptions.h> #include <package/manager/Exceptions.h>
#include <package/solver/SolverPackage.h> #include <package/solver/SolverPackage.h>
#include <AutoDeleter.h>
#include <Catalog.h> #include <Catalog.h>
#include "Alert.h" #include "Alert.h"
@@ -38,13 +39,12 @@ using namespace BPackageKit::BManager::BPrivate;
using BPackageKit::BSolverPackage; using BPackageKit::BSolverPackage;
UninstallPackageProcess::UninstallPackageProcess(PackageInfoRef package, UninstallPackageProcess::UninstallPackageProcess(const BString& packageName, Model* model)
Model* model)
: :
AbstractPackageProcess(package, model) AbstractPackageProcess(packageName, model)
{ {
fDescription = B_TRANSLATE("Uninstalling \"%PackageName%\""); fDescription = B_TRANSLATE("Uninstalling \"%PackageName%\"");
fDescription.ReplaceAll("%PackageName%", fPackage->Name()); fDescription.ReplaceAll("%PackageName%", packageName);
} }
@@ -70,67 +70,68 @@ UninstallPackageProcess::Description() const
status_t status_t
UninstallPackageProcess::RunInternal() UninstallPackageProcess::RunInternal()
{ {
fPackageManager->Init(BPackageManager::B_ADD_INSTALLED_REPOSITORIES); // TODO: ideally if the package is installed at multiple locations,
// the user should be able to pick which one to remove.
PackageInfoRef ref(fPackage); PackageManager* packageManager = new(std::nothrow)
PackageState state = PackageUtils::State(fPackage); PackageManager(static_cast<BPackageInstallationLocation>(InstallLocation()));
const char* packageName = ref->Name().String(); ObjectDeleter<PackageManager> solverDeleter(packageManager);
fPackageManager->SetCurrentActionPackage(ref, false); packageManager->Init(BPackageManager::B_ADD_INSTALLED_REPOSITORIES);
fPackageManager->AddProgressListener(this);
PackageInfoRef package = FindPackageByName(fPackageName);
PackageState state = PackageUtils::State(package);
packageManager->SetCurrentActionPackage(package, false);
packageManager->AddProgressListener(this);
const char* packageNameString = package->Name().String();
try { try {
fPackageManager->Uninstall(&packageName, 1); packageManager->Uninstall(&packageNameString, 1);
} catch (BFatalErrorException& ex) { } catch (BFatalErrorException& ex) {
BString errorString; BString errorString;
errorString.SetToFormat( errorString.SetToFormat("Fatal error occurred while uninstalling package %s: %s (%s)\n",
"Fatal error occurred while uninstalling package %s: " fPackageName.String(), ex.Message().String(), ex.Details().String());
"%s (%s)\n", packageName, ex.Message().String(), AppUtils::NotifySimpleError(B_TRANSLATE("Fatal error"), errorString, B_STOP_ALERT);
ex.Details().String()); SetPackageState(fPackageName, state);
AppUtils::NotifySimpleError(B_TRANSLATE("Fatal error"), errorString,
B_STOP_ALERT);
SetPackageState(ref, state);
return ex.Error(); return ex.Error();
} catch (BAbortedByUserException& ex) { } catch (BAbortedByUserException& ex) {
return B_OK; return B_OK;
} catch (BNothingToDoException& ex) { } catch (BNothingToDoException& ex) {
return B_OK; return B_OK;
} catch (BException& ex) { } catch (BException& ex) {
HDERROR("Exception occurred while uninstalling package %s: %s", HDERROR("Exception occurred while uninstalling package %s: %s", fPackageName.String(),
packageName, ex.Message().String()); ex.Message().String());
SetPackageState(ref, state); SetPackageState(fPackageName, state);
return B_ERROR; return B_ERROR;
} }
fPackageManager->RemoveProgressListener(this); packageManager->RemoveProgressListener(this);
return B_OK; return B_OK;
} }
void void
UninstallPackageProcess::StartApplyingChanges( UninstallPackageProcess::StartApplyingChanges(BPackageManager::InstalledRepository& repository)
BPackageManager::InstalledRepository& repository)
{ {
BPackageManager::InstalledRepository::PackageList& packages BPackageManager::InstalledRepository::PackageList& packages = repository.PackagesToDeactivate();
= repository.PackagesToDeactivate();
for (int32 i = 0; i < packages.CountItems(); i++) { for (int32 i = 0; i < packages.CountItems(); i++) {
PackageInfoRef ref(FindPackageByName(packages.ItemAt(i) BString packageName = packages.ItemAt(i)->Name();
->Name())); fRemovedPackageNames.insert(packageName);
if (ref.IsSet())
fRemovedPackages.push_back(ref);
} }
} }
void void
UninstallPackageProcess::ApplyingChangesDone( UninstallPackageProcess::ApplyingChangesDone(BPackageManager::InstalledRepository& repository)
BPackageManager::InstalledRepository& repository)
{ {
std::vector<PackageInfoRef>::iterator it; std::set<BString>::const_iterator it;
for (it = fRemovedPackages.begin(); it != fRemovedPackages.end(); it++) { for (it = fRemovedPackageNames.begin(); it != fRemovedPackageNames.end(); it++) {
PackageInfoRef packageInfoRef = *it; BString packageName = *it;
SetPackageState(packageInfoRef, NONE); SetPackageState(packageName, NONE);
ClearPackageInstallationLocations(packageInfoRef); ClearPackageInstallationLocations(packageName);
} }
} }
@@ -3,7 +3,7 @@
* Copyright 2011, Ingo Weinhold, <[email protected]> * Copyright 2011, Ingo Weinhold, <[email protected]>
* Copyright 2013, Rene Gollent, <[email protected]> * Copyright 2013, Rene Gollent, <[email protected]>
* Copyright 2017, Julian Harnath <[email protected]> * Copyright 2017, Julian Harnath <[email protected]>
* Copyright 2021, Andrew Lindesay <[email protected]> * Copyright 2021-2025, Andrew Lindesay <[email protected]>
* *
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
* *
@@ -23,7 +23,7 @@ class UninstallPackageProcess
private PackageProgressListener { private PackageProgressListener {
public: public:
UninstallPackageProcess( UninstallPackageProcess(
PackageInfoRef package, Model* model); const BString& packageName, Model* model);
virtual ~UninstallPackageProcess(); virtual ~UninstallPackageProcess();
virtual const char* Name() const; virtual const char* Name() const;
@@ -41,8 +41,7 @@ protected:
private: private:
BString fDescription; BString fDescription;
std::vector<PackageInfoRef> std::set<BString> fRemovedPackageNames;
fRemovedPackages;
}; };
#endif // INSTALL_PACKAGE_PROCESS_H #endif // INSTALL_PACKAGE_PROCESS_H
+121 -146
View File
@@ -1,7 +1,7 @@
/* /*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>. * Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2013, Rene Gollent <[email protected]>. * Copyright 2013, Rene Gollent <[email protected]>.
* Copyright 2016-2023, Andrew Lindesay <[email protected]>. * Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
@@ -14,27 +14,6 @@
#include "PackageUtils.h" #include "PackageUtils.h"
// #pragma mark - Sorting Functions
static bool
_IsPackageBeforeByName(const PackageInfoRef& p1, const BString& packageName)
{
return p1->Name().Compare(packageName) < 0;
}
/*! This function is used in order to provide an ordering on the packages
that are stored on a Depot.
*/
static bool
_IsPackageBefore(const PackageInfoRef& p1, const PackageInfoRef& p2)
{
return _IsPackageBeforeByName(p1, p2->Name());
}
// #pragma mark - Class implementation // #pragma mark - Class implementation
@@ -61,31 +40,16 @@ DepotInfo::DepotInfo(const DepotInfo& other)
: :
fName(other.fName), fName(other.fName),
fIdentifier(other.fIdentifier), fIdentifier(other.fIdentifier),
fPackages(other.fPackages),
fWebAppRepositoryCode(other.fWebAppRepositoryCode), fWebAppRepositoryCode(other.fWebAppRepositoryCode),
fWebAppRepositorySourceCode(other.fWebAppRepositorySourceCode) fWebAppRepositorySourceCode(other.fWebAppRepositorySourceCode)
{ {
} }
DepotInfo&
DepotInfo::operator=(const DepotInfo& other)
{
fName = other.fName;
fIdentifier = other.fIdentifier;
fPackages = other.fPackages;
fWebAppRepositoryCode = other.fWebAppRepositoryCode;
fWebAppRepositorySourceCode = other.fWebAppRepositorySourceCode;
return *this;
}
bool bool
DepotInfo::operator==(const DepotInfo& other) const DepotInfo::operator==(const DepotInfo& other) const
{ {
return fName == other.fName return fName == other.fName && fIdentifier == fIdentifier;
&& fIdentifier == fIdentifier
&& fPackages == other.fPackages;
} }
@@ -96,127 +60,31 @@ DepotInfo::operator!=(const DepotInfo& other) const
} }
int32 const BString&
DepotInfo::CountPackages() const DepotInfo::Name() const
{ {
return fPackages.size(); return fName;
} }
PackageInfoRef const BString&
DepotInfo::PackageAtIndex(int32 index) DepotInfo::Identifier() const
{ {
return fPackages[index]; return fIdentifier;
} }
/*! This method will insert the package into the list of packages const BString&
in order so that the list of packages remains in order. DepotInfo::WebAppRepositoryCode() const
*/
void
DepotInfo::AddPackage(PackageInfoRef& package)
{ {
std::vector<PackageInfoRef>::iterator itInsertionPt return fWebAppRepositoryCode;
= std::lower_bound(
fPackages.begin(),
fPackages.end(),
package,
&_IsPackageBefore);
if (fPackages.end() != itInsertionPt && (*itInsertionPt)->Name() == package->Name())
*itInsertionPt = package;
else
fPackages.insert(itInsertionPt, package);
} }
bool const BString&
DepotInfo::HasPackage(const BString& packageName) DepotInfo::WebAppRepositorySourceCode() const
{ {
std::vector<PackageInfoRef>::const_iterator it return fWebAppRepositorySourceCode;
= std::lower_bound(
fPackages.begin(),
fPackages.end(),
packageName,
&_IsPackageBeforeByName);
if (it != fPackages.end()) {
PackageInfoRef candidate = *it;
return (candidate.Get() != NULL
&& candidate.Get()->Name() == packageName);
}
return false;
}
PackageInfoRef
DepotInfo::PackageByName(const BString& packageName)
{
std::vector<PackageInfoRef>::const_iterator it
= std::lower_bound(
fPackages.begin(),
fPackages.end(),
packageName,
&_IsPackageBeforeByName);
if (it != fPackages.end()) {
PackageInfoRef candidate = *it;
if (candidate.Get() != NULL && candidate.Get()->Name() == packageName)
return candidate;
}
return PackageInfoRef();
}
void
DepotInfo::SyncPackagesFromDepot(const DepotInfoRef& other)
{
for (int32 i = other->CountPackages() - 1; i >= 0; i--) {
PackageInfoRef otherPackage = other->PackageAtIndex(i);
PackageInfoRef myPackage = PackageByName(otherPackage->Name());
if (myPackage.IsSet()) {
PackageLocalInfoRef localInfo = PackageUtils::NewLocalInfo(myPackage);
PackageLocalInfoRef otherLocalInfo = otherPackage->LocalInfo();
if (otherLocalInfo.IsSet()) {
localInfo->SetState(otherLocalInfo->State());
localInfo->SetLocalFilePath(otherLocalInfo->LocalFilePath());
localInfo->SetSystemDependency(otherLocalInfo->IsSystemDependency());
}
myPackage->SetLocalInfo(localInfo);
} else {
HDINFO("%s: new package: '%s'", fName.String(),
otherPackage->Name().String());
AddPackage(otherPackage);
}
}
for (int32 i = CountPackages() - 1; i >= 0; i--) {
PackageInfoRef myPackage = PackageAtIndex(i);
if (!other->HasPackage(myPackage->Name())) {
HDINFO("%s: removing package: '%s'", fName.String(),
myPackage->Name().String());
fPackages.erase(fPackages.begin() + i);
}
}
}
bool
DepotInfo::HasAnyProminentPackages() const
{
std::vector<PackageInfoRef>::const_iterator it;
for (it = fPackages.begin(); it != fPackages.end(); it++) {
const PackageInfoRef& package = *it;
const PackageClassificationInfoRef& classification = package->PackageClassificationInfo();
if (classification.IsSet()) {
if (classification->IsProminent())
return true;
}
}
return false;
} }
@@ -239,3 +107,110 @@ DepotInfo::SetWebAppRepositorySourceCode(const BString& code)
{ {
fWebAppRepositorySourceCode = code; fWebAppRepositorySourceCode = code;
} }
// #pragma mark - DepotInfoBuilder
DepotInfoBuilder::DepotInfoBuilder()
:
fName(),
fIdentifier(),
fWebAppRepositoryCode(),
fWebAppRepositorySourceCode()
{
}
DepotInfoBuilder::DepotInfoBuilder(const DepotInfoRef& value)
:
fName(),
fIdentifier(),
fWebAppRepositoryCode(),
fWebAppRepositorySourceCode()
{
fSource = value;
}
DepotInfoBuilder::~DepotInfoBuilder()
{
}
void
DepotInfoBuilder::_InitFromSource()
{
if (fSource.IsSet()) {
_Init(fSource.Get());
fSource.Unset();
}
}
void
DepotInfoBuilder::_Init(const DepotInfo* value)
{
fName = value->Name();
fIdentifier = value->Identifier();
fWebAppRepositoryCode = value->WebAppRepositoryCode();
fWebAppRepositorySourceCode = value->WebAppRepositorySourceCode();
}
DepotInfoRef
DepotInfoBuilder::BuildRef() const
{
if (fSource.IsSet())
return fSource;
DepotInfo* depotInfo = new DepotInfo(fName);
depotInfo->SetIdentifier(fIdentifier);
depotInfo->SetWebAppRepositoryCode(fWebAppRepositoryCode);
depotInfo->SetWebAppRepositorySourceCode(fWebAppRepositorySourceCode);
return DepotInfoRef(depotInfo, true);
}
DepotInfoBuilder&
DepotInfoBuilder::WithName(const BString& value)
{
if (!fSource.IsSet() || fSource->Name() != value) {
_InitFromSource();
fName = value;
}
return *this;
}
DepotInfoBuilder&
DepotInfoBuilder::WithIdentifier(const BString& value)
{
if (!fSource.IsSet() || fSource->Identifier() != value) {
_InitFromSource();
fIdentifier = value;
}
return *this;
}
DepotInfoBuilder&
DepotInfoBuilder::WithWebAppRepositoryCode(const BString& value)
{
if (!fSource.IsSet() || fSource->WebAppRepositoryCode() != value) {
_InitFromSource();
fWebAppRepositoryCode = value;
}
return *this;
}
DepotInfoBuilder&
DepotInfoBuilder::WithWebAppRepositorySourceCode(const BString& value)
{
if (!fSource.IsSet() || fSource->WebAppRepositorySourceCode() != value) {
_InitFromSource();
fWebAppRepositorySourceCode = value;
}
return *this;
}
+42 -27
View File
@@ -1,6 +1,6 @@
/* /*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>. * Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2016-2023, Andrew Lindesay <[email protected]>. * Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef DEPOT_INFO_H #ifndef DEPOT_INFO_H
@@ -14,48 +14,36 @@
#include "PackageInfo.h" #include "PackageInfo.h"
class DepotInfoBuilder;
/*! Instances of this class should not be created directly; instead use the
DepotInfoBuilder class as a builder-constructor.
*/
class DepotInfo : public BReferenceable { class DepotInfo : public BReferenceable {
friend class DepotInfoBuilder;
public: public:
DepotInfo(); DepotInfo();
DepotInfo(const BString& name); DepotInfo(const BString& name);
DepotInfo(const DepotInfo& other); DepotInfo(const DepotInfo& other);
DepotInfo& operator=(const DepotInfo& other);
bool operator==(const DepotInfo& other) const; bool operator==(const DepotInfo& other) const;
bool operator!=(const DepotInfo& other) const; bool operator!=(const DepotInfo& other) const;
const BString& Name() const const BString& Name() const;
{ return fName; } const BString& Identifier() const;
const BString& WebAppRepositoryCode() const;
const BString& WebAppRepositorySourceCode() const;
private:
void SetIdentifier(const BString& value); void SetIdentifier(const BString& value);
const BString& Identifier() const
{ return fIdentifier; }
int32 CountPackages() const;
PackageInfoRef PackageAtIndex(int32 index);
void AddPackage(PackageInfoRef& package);
PackageInfoRef PackageByName(const BString& packageName);
bool HasPackage(const BString& packageName);
void SyncPackagesFromDepot(
const BReference<DepotInfo>& other);
bool HasAnyProminentPackages() const;
void SetWebAppRepositoryCode(const BString& code); void SetWebAppRepositoryCode(const BString& code);
const BString& WebAppRepositoryCode() const void SetWebAppRepositorySourceCode(const BString& code);
{ return fWebAppRepositoryCode; }
void SetWebAppRepositorySourceCode(
const BString& code);
const BString& WebAppRepositorySourceCode() const
{ return fWebAppRepositorySourceCode; }
private: private:
BString fName; BString fName;
BString fIdentifier; BString fIdentifier;
std::vector<PackageInfoRef>
fPackages;
BString fWebAppRepositoryCode; BString fWebAppRepositoryCode;
BString fWebAppRepositorySourceCode; BString fWebAppRepositorySourceCode;
}; };
@@ -64,4 +52,31 @@ private:
typedef BReference<DepotInfo> DepotInfoRef; typedef BReference<DepotInfo> DepotInfoRef;
class DepotInfoBuilder
{
public:
DepotInfoBuilder();
DepotInfoBuilder(const DepotInfoRef& value);
virtual ~DepotInfoBuilder();
DepotInfoRef BuildRef() const;
DepotInfoBuilder& WithName(const BString& value);
DepotInfoBuilder& WithIdentifier(const BString& value);
DepotInfoBuilder& WithWebAppRepositoryCode(const BString& value);
DepotInfoBuilder& WithWebAppRepositorySourceCode(const BString& value);
private:
void _InitFromSource();
void _Init(const DepotInfo* value);
private:
DepotInfoRef fSource;
BString fName;
BString fIdentifier;
BString fWebAppRepositoryCode;
BString fWebAppRepositorySourceCode;
};
#endif // DEPOT_INFO_H #endif // DEPOT_INFO_H
+27 -7
View File
@@ -1,7 +1,7 @@
/* /*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>. * Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2013, Rene Gollent <[email protected]>. * Copyright 2013, Rene Gollent <[email protected]>.
* Copyright 2016-2024, Andrew Lindesay <[email protected]>. * Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
@@ -13,11 +13,10 @@
#include "StringUtils.h" #include "StringUtils.h"
/*! serverName is the name of the language on the server. /*! @param serverName is the name of the language on the server.
*/ */
Language::Language(const char* language, const BString& serverName, Language::Language(const char* language, const BString& serverName, bool isPopular)
bool isPopular)
: :
BLanguage(language), BLanguage(language),
fServerName(serverName), fServerName(serverName),
@@ -35,6 +34,27 @@ Language::Language(const Language& other)
} }
bool
Language::operator==(const Language& other) const
{
return Compare(other) == 0;
}
bool
Language::operator!=(const Language& other) const
{
return !(*this == other);
}
bool
Language::operator<(const Language& other) const
{
return Compare(other) < 0;
}
status_t status_t
Language::GetName(BString& name, const BLanguage* displayLanguage) const Language::GetName(BString& name, const BLanguage* displayLanguage) const
{ {
@@ -60,9 +80,9 @@ Language::Compare(const Language& other) const
bool bool
IsLanguageBefore(const LanguageRef& l1, const LanguageRef& l2) IsLanguageRefLess(const LanguageRef& l1, const LanguageRef& l2)
{ {
if (!l1.IsSet() || !l2.IsSet()) if (!l1.IsSet() || !l2.IsSet())
HDFATAL("unexpected NULL reference in a referencable"); debugger("illegal state in language less");
return l1.Get()->Compare(*(l2.Get())) < 0; return *(l1.Get()) < *(l2.Get());
} }
+10 -2
View File
@@ -1,6 +1,6 @@
/* /*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>. * Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2016-2023, Andrew Lindesay <[email protected]>. * Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef LANGUAGE_H #ifndef LANGUAGE_H
@@ -14,6 +14,10 @@
/*! This class represents a language that is supported by the Haiku /*! This class represents a language that is supported by the Haiku
Depot Server system. This may differ from the set of languages Depot Server system. This may differ from the set of languages
that are supported in the platform itself. that are supported in the platform itself.
No builder is provided for this class because it sub-classes from
a Haiku type. Instead always create new instances with the constructor
and do not use any mutating functions offered by the superclass.
*/ */
class Language : public BReferenceable, public BLanguage { class Language : public BReferenceable, public BLanguage {
@@ -23,6 +27,10 @@ public:
bool isPopular); bool isPopular);
Language(const Language& other); Language(const Language& other);
bool operator<(const Language& other) const;
bool operator==(const Language& other) const;
bool operator!=(const Language& other) const;
status_t GetName(BString& name, status_t GetName(BString& name,
const BLanguage* displayLanguage = NULL const BLanguage* displayLanguage = NULL
) const; ) const;
@@ -40,7 +48,7 @@ private:
typedef BReference<Language> LanguageRef; typedef BReference<Language> LanguageRef;
extern bool IsLanguageBefore(const LanguageRef& l1, const LanguageRef& l2); extern bool IsLanguageRefLess(const LanguageRef& l1, const LanguageRef& l2);
#endif // LANGUAGE_H #endif // LANGUAGE_H
@@ -1,7 +1,7 @@
/* /*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>. * Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2013, Rene Gollent <[email protected]>. * Copyright 2013, Rene Gollent <[email protected]>.
* Copyright 2016-2023, Andrew Lindesay <[email protected]>. * Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
@@ -41,12 +41,10 @@ PackageCategory::PackageCategory(const PackageCategory& other)
} }
PackageCategory& bool
PackageCategory::operator=(const PackageCategory& other) PackageCategory::operator<(const PackageCategory& other) const
{ {
fCode = other.fCode; return Compare(other) < 0;
fName = other.fName;
return *this;
} }
@@ -68,18 +66,17 @@ int
PackageCategory::Compare(const PackageCategory& other) const PackageCategory::Compare(const PackageCategory& other) const
{ {
BCollator* collator = LocaleUtils::GetSharedCollator(); BCollator* collator = LocaleUtils::GetSharedCollator();
int32 result = collator->Compare(Name().String(), int32 result = collator->Compare(Name().String(), other.Name().String());
other.Name().String());
if (result == 0) if (result == 0)
result = Code().Compare(other.Code()); result = Code().Compare(other.Code());
return result; return result;
} }
bool IsPackageCategoryBefore(const CategoryRef& c1, bool
const CategoryRef& c2) IsPackageCategoryRefLess(const CategoryRef& c1, const CategoryRef& c2)
{ {
if (!c1.IsSet() || !c2.IsSet()) if (!c1.IsSet() || !c2.IsSet())
HDFATAL("unexpected NULL reference in a referencable"); HDFATAL("illegal state in package category less");
return c1->Compare(*c2) < 0; return *(c1.Get()) < *(c2.Get());
} }
@@ -1,6 +1,6 @@
/* /*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>. * Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2016-2023, Andrew Lindesay <[email protected]>. * Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef PACKAGE_CATEGORY_H #ifndef PACKAGE_CATEGORY_H
@@ -11,6 +11,13 @@
#include <String.h> #include <String.h>
/*! This class represents a category into which a package may be assigned. The
`code` corresponds to a code on the server.
No builder is provided for this class; always create new instances using
the constructor.
*/
class PackageCategory : public BReferenceable { class PackageCategory : public BReferenceable {
public: public:
PackageCategory(); PackageCategory();
@@ -18,7 +25,7 @@ public:
const BString& name); const BString& name);
PackageCategory(const PackageCategory& other); PackageCategory(const PackageCategory& other);
PackageCategory& operator=(const PackageCategory& other); bool operator<(const PackageCategory& other) const;
bool operator==(const PackageCategory& other) const; bool operator==(const PackageCategory& other) const;
bool operator!=(const PackageCategory& other) const; bool operator!=(const PackageCategory& other) const;
@@ -38,7 +45,7 @@ private:
typedef BReference<PackageCategory> CategoryRef; typedef BReference<PackageCategory> CategoryRef;
extern bool IsPackageCategoryBefore(const CategoryRef& c1, const CategoryRef& c2); extern bool IsPackageCategoryRefLess(const CategoryRef& c1, const CategoryRef& c2);
#endif // PACKAGE_CATEGORY_H #endif // PACKAGE_CATEGORY_H
@@ -1,5 +1,5 @@
/* /*
* Copyright 2024, Andrew Lindesay <[email protected]>. * Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
@@ -14,7 +14,7 @@
PackageClassificationInfo::PackageClassificationInfo() PackageClassificationInfo::PackageClassificationInfo()
: :
fCategories(), fCategories(),
fProminence(-1L), fProminence(PROMINANCE_ORDERING_NONE),
fIsNativeDesktop(false) fIsNativeDesktop(false)
{ {
} }
@@ -29,6 +29,20 @@ PackageClassificationInfo::PackageClassificationInfo(const PackageClassification
} }
uint32
PackageClassificationInfo::Prominence() const
{
return fProminence;
}
bool
PackageClassificationInfo::HasProminence() const
{
return fProminence != 0;
}
int32 int32
PackageClassificationInfo::CountCategories() const PackageClassificationInfo::CountCategories() const
{ {
@@ -36,26 +50,18 @@ PackageClassificationInfo::CountCategories() const
} }
CategoryRef const CategoryRef
PackageClassificationInfo::CategoryAtIndex(int32 index) const PackageClassificationInfo::CategoryAtIndex(int32 index) const
{ {
return fCategories[index]; return fCategories[index];
} }
void
PackageClassificationInfo::ClearCategories()
{
if (!fCategories.empty())
fCategories.clear();
}
bool bool
PackageClassificationInfo::AddCategory(const CategoryRef& category) PackageClassificationInfo::AddCategory(const CategoryRef& category)
{ {
std::vector<CategoryRef>::const_iterator itInsertionPt = std::lower_bound(fCategories.begin(), std::vector<CategoryRef>::const_iterator itInsertionPt = std::lower_bound(fCategories.begin(),
fCategories.end(), category, &IsPackageCategoryBefore); fCategories.end(), category, &IsPackageCategoryRefLess);
if (itInsertionPt == fCategories.end()) { if (itInsertionPt == fCategories.end()) {
fCategories.push_back(category); fCategories.push_back(category);
@@ -131,3 +137,104 @@ PackageClassificationInfo::operator!=(const PackageClassificationInfo& other) co
{ {
return !(*this == other); return !(*this == other);
} }
// #pragma mark - PackageClassificationInfoBuilder
PackageClassificationInfoBuilder::PackageClassificationInfoBuilder()
:
fCategories(),
fProminence(-1L),
fIsNativeDesktop(false)
{
}
PackageClassificationInfoBuilder::PackageClassificationInfoBuilder(
const PackageClassificationInfoRef& value)
:
fCategories(),
fProminence(PROMINANCE_ORDERING_NONE),
fIsNativeDesktop(false)
{
fSource = value;
}
PackageClassificationInfoBuilder::~PackageClassificationInfoBuilder()
{
}
void
PackageClassificationInfoBuilder::_InitFromSource()
{
if (fSource.IsSet()) {
_Init(fSource.Get());
fSource.Unset();
}
}
void
PackageClassificationInfoBuilder::_Init(const PackageClassificationInfo* value)
{
for (int32 i = value->CountCategories() - 1; i >= 0; i--)
fCategories.push_back(value->CategoryAtIndex(i));
fProminence = value->Prominence();
fIsNativeDesktop = value->IsNativeDesktop();
}
PackageClassificationInfoRef
PackageClassificationInfoBuilder::BuildRef() const
{
if (fSource.IsSet())
return fSource;
PackageClassificationInfo* classificationInfo = new PackageClassificationInfo();
classificationInfo->SetProminence(fProminence);
classificationInfo->SetIsNativeDesktop(fIsNativeDesktop);
std::vector<CategoryRef>::const_iterator it;
for (it = fCategories.begin(); it != fCategories.end(); it++)
classificationInfo->AddCategory(*it);
return PackageClassificationInfoRef(classificationInfo, true);
}
PackageClassificationInfoBuilder&
PackageClassificationInfoBuilder::WithProminence(uint32 prominence)
{
if (!fSource.IsSet() || fSource->Prominence() != prominence) {
_InitFromSource();
fProminence = prominence;
}
return *this;
}
PackageClassificationInfoBuilder&
PackageClassificationInfoBuilder::WithIsNativeDesktop(bool value)
{
if (!fSource.IsSet() || fSource->IsNativeDesktop() != value) {
_InitFromSource();
fIsNativeDesktop = value;
}
return *this;
}
PackageClassificationInfoBuilder&
PackageClassificationInfoBuilder::AddCategory(const CategoryRef& category)
{
if (!fSource.IsSet() || fSource->HasCategoryByCode(category->Code())) {
_InitFromSource();
fCategories.push_back(category);
}
return *this;
}
@@ -1,5 +1,5 @@
/* /*
* Copyright 2024, Andrew Lindesay <[email protected]>. * Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef PACKAGE_CLASSIFICATION_INFO_H #ifndef PACKAGE_CLASSIFICATION_INFO_H
@@ -13,8 +13,16 @@
#include "PackageCategory.h" #include "PackageCategory.h"
class PackageClassificationInfoBuilder;
/*! Instances of this class should not be created directly; instead use the
PackageClassificationInfoBuilder class as a builder-constructor.
*/
class PackageClassificationInfo : public BReferenceable class PackageClassificationInfo : public BReferenceable
{ {
friend class PackageClassificationInfoBuilder;
public: public:
PackageClassificationInfo(); PackageClassificationInfo();
PackageClassificationInfo(const PackageClassificationInfo& other); PackageClassificationInfo(const PackageClassificationInfo& other);
@@ -22,20 +30,20 @@ public:
bool operator==(const PackageClassificationInfo& other) const; bool operator==(const PackageClassificationInfo& other) const;
bool operator!=(const PackageClassificationInfo& other) const; bool operator!=(const PackageClassificationInfo& other) const;
void SetProminence(uint32 prominence); uint32 Prominence() const;
uint32 Prominence() const bool HasProminence() const;
{ return fProminence; }
bool HasProminence() const
{ return fProminence != 0; }
bool IsProminent() const; bool IsProminent() const;
void ClearCategories();
bool AddCategory(const CategoryRef& category);
int32 CountCategories() const; int32 CountCategories() const;
CategoryRef CategoryAtIndex(int32 index) const; const CategoryRef CategoryAtIndex(int32 index) const;
bool HasCategoryByCode(const BString& code) const; bool HasCategoryByCode(const BString& code) const;
bool IsNativeDesktop() const; bool IsNativeDesktop() const;
private:
void SetProminence(uint32 prominence);
void ClearCategories();
bool AddCategory(const CategoryRef& category);
void SetIsNativeDesktop(bool value); void SetIsNativeDesktop(bool value);
private: private:
@@ -49,4 +57,37 @@ private:
typedef BReference<PackageClassificationInfo> PackageClassificationInfoRef; typedef BReference<PackageClassificationInfo> PackageClassificationInfoRef;
class PackageClassificationInfoBuilder
{
public:
PackageClassificationInfoBuilder();
PackageClassificationInfoBuilder(
const PackageClassificationInfoRef& value);
virtual ~PackageClassificationInfoBuilder();
PackageClassificationInfoRef
BuildRef() const;
PackageClassificationInfoBuilder&
WithProminence(uint32 prominence);
PackageClassificationInfoBuilder&
WithIsNativeDesktop(bool value);
PackageClassificationInfoBuilder&
AddCategory(const CategoryRef& category);
private:
void _InitFromSource();
void _Init(const PackageClassificationInfo* value);
private:
PackageClassificationInfoRef
fSource;
std::vector<CategoryRef>
fCategories;
uint32 fProminence;
bool fIsNativeDesktop;
};
#endif // PACKAGE_CLASSIFICATION_INFO_H #endif // PACKAGE_CLASSIFICATION_INFO_H
@@ -1,5 +1,5 @@
/* /*
* Copyright 2024, Andrew Lindesay <[email protected]>. * Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
@@ -34,18 +34,6 @@ PackageCoreInfo::~PackageCoreInfo()
} }
PackageCoreInfo&
PackageCoreInfo::operator=(const PackageCoreInfo& other)
{
fVersion = other.fVersion;
fPublisher = other.fPublisher;
fArchitecture = other.fArchitecture;
fDepotName = other.fDepotName;
return *this;
}
bool bool
PackageCoreInfo::operator==(const PackageCoreInfo& other) const PackageCoreInfo::operator==(const PackageCoreInfo& other) const
{ {
@@ -63,6 +51,34 @@ PackageCoreInfo::operator!=(const PackageCoreInfo& other) const
} }
const PackageVersionRef&
PackageCoreInfo::Version() const
{
return fVersion;
}
const PackagePublisherInfoRef&
PackageCoreInfo::Publisher() const
{
return fPublisher;
}
const BString
PackageCoreInfo::Architecture() const
{
return fArchitecture;
}
const BString&
PackageCoreInfo::DepotName() const
{
return fDepotName;
}
void void
PackageCoreInfo::SetVersion(PackageVersionRef value) PackageCoreInfo::SetVersion(PackageVersionRef value)
{ {
@@ -89,3 +105,112 @@ PackageCoreInfo::SetDepotName(const BString& value)
{ {
fDepotName = value; fDepotName = value;
} }
// #pragma mark - PackageCoreInfoBuilder
PackageCoreInfoBuilder::PackageCoreInfoBuilder()
:
fVersion(),
fPublisher(),
fArchitecture(),
fDepotName()
{
}
PackageCoreInfoBuilder::PackageCoreInfoBuilder(const PackageCoreInfoRef& other)
:
fVersion(),
fPublisher(),
fArchitecture(),
fDepotName()
{
fSource = other;
}
PackageCoreInfoBuilder::~PackageCoreInfoBuilder()
{
}
void
PackageCoreInfoBuilder::_InitFromSource()
{
if (fSource.IsSet()) {
_Init(fSource.Get());
fSource.Unset();
}
}
void
PackageCoreInfoBuilder::_Init(const PackageCoreInfo* other)
{
fVersion = other->Version();
fPublisher = other->Publisher();
fArchitecture = other->Architecture();
fDepotName = other->DepotName();
}
PackageCoreInfoRef
PackageCoreInfoBuilder::BuildRef()
{
if (fSource.IsSet())
return fSource;
PackageCoreInfo* coreInfo = new PackageCoreInfo();
coreInfo->SetVersion(fVersion);
coreInfo->SetPublisher(fPublisher);
coreInfo->SetArchitecture(fArchitecture);
coreInfo->SetDepotName(fDepotName);
return PackageCoreInfoRef(coreInfo, true);
}
PackageCoreInfoBuilder&
PackageCoreInfoBuilder::WithVersion(PackageVersionRef value)
{
if (!fSource.IsSet() || fSource->Version() != value) {
_InitFromSource();
fVersion = value;
}
return *this;
}
PackageCoreInfoBuilder&
PackageCoreInfoBuilder::WithPublisher(PackagePublisherInfoRef value)
{
if (!fSource.IsSet() || fSource->Publisher() != value) {
_InitFromSource();
fPublisher = value;
}
return *this;
}
PackageCoreInfoBuilder&
PackageCoreInfoBuilder::WithArchitecture(const BString& value)
{
if (!fSource.IsSet() || fSource->Architecture() != value) {
_InitFromSource();
fArchitecture = value;
}
return *this;
}
PackageCoreInfoBuilder&
PackageCoreInfoBuilder::WithDepotName(const BString& value)
{
if (!fSource.IsSet() || fSource->DepotName() != value) {
_InitFromSource();
fDepotName = value;
}
return *this;
}
@@ -1,5 +1,5 @@
/* /*
* Copyright 2024, Andrew Lindesay <[email protected]>. * Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef PACKAGE_CORE_INFO_H #ifndef PACKAGE_CORE_INFO_H
@@ -15,38 +15,79 @@
using BPackageKit::BPackageVersion; using BPackageKit::BPackageVersion;
class PackageCoreInfoBuilder;
/*! This class models the core data for the package. /*! This class models the core data for the package.
Instances of this class should not be created directly; instead use the
PackageCoreInfoBuilder class as a builder-constructor.
*/ */
class PackageCoreInfo : public BReferenceable class PackageCoreInfo : public BReferenceable
{ {
friend class PackageCoreInfoBuilder;
public: public:
PackageCoreInfo(); PackageCoreInfo();
PackageCoreInfo(const PackageCoreInfo& other); PackageCoreInfo(const PackageCoreInfo& other);
virtual ~PackageCoreInfo(); virtual ~PackageCoreInfo();
PackageCoreInfo& operator=(const PackageCoreInfo& other);
bool operator==(const PackageCoreInfo& other) const; bool operator==(const PackageCoreInfo& other) const;
bool operator!=(const PackageCoreInfo& other) const; bool operator!=(const PackageCoreInfo& other) const;
const PackageVersionRef& const PackageVersionRef&
Version() const Version() const;
{ return fVersion; }
void SetVersion(PackageVersionRef value);
const PackagePublisherInfoRef& const PackagePublisherInfoRef&
Publisher() const Publisher() const;
{ return fPublisher; } const BString Architecture() const;
void SetPublisher(PackagePublisherInfoRef value); const BString& DepotName() const;
const BString Architecture() const
{ return fArchitecture; }
void SetArchitecture(const BString& value);
void SetDepotName(const BString& value);
const BString& DepotName() const
{ return fDepotName; }
private: private:
void SetVersion(PackageVersionRef value);
void SetPublisher(PackagePublisherInfoRef value);
void SetArchitecture(const BString& value);
void SetDepotName(const BString& value);
private:
PackageVersionRef
fVersion;
PackagePublisherInfoRef
fPublisher;
BString fArchitecture;
BString fDepotName;
// The name and not the identifier are used here because the
// identifier is not readily available in all API situations.
};
typedef BReference<PackageCoreInfo> PackageCoreInfoRef;
class PackageCoreInfoBuilder
{
public:
PackageCoreInfoBuilder();
PackageCoreInfoBuilder(const PackageCoreInfoRef& other);
virtual ~PackageCoreInfoBuilder();
PackageCoreInfoRef BuildRef();
PackageCoreInfoBuilder&
WithVersion(PackageVersionRef value);
PackageCoreInfoBuilder&
WithPublisher(PackagePublisherInfoRef value);
PackageCoreInfoBuilder&
WithArchitecture(const BString& value);
PackageCoreInfoBuilder&
WithDepotName(const BString& value);
private:
void _InitFromSource();
void _Init(const PackageCoreInfo* value);
private:
PackageCoreInfoRef
fSource;
PackageVersionRef PackageVersionRef
fVersion; fVersion;
PackagePublisherInfoRef PackagePublisherInfoRef
@@ -56,7 +97,4 @@ private:
}; };
typedef BReference<PackageCoreInfo> PackageCoreInfoRef;
#endif // PACKAGE_CORE_INFO_H #endif // PACKAGE_CORE_INFO_H
@@ -1,15 +1,16 @@
/* /*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>. * Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2014, Axel Dörfler <[email protected]>. * Copyright 2014, Axel Dörfler <[email protected]>.
* Copyright 2016-2024, Andrew Lindesay <[email protected]>. * Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
* *
* Note that this file included code earlier from `Model.cpp` and * Note that this file included code earlier from `Model.cpp` and
* copyrights have been latterly been carried across in 2024. * copyrights have been latterly been carried across in 2024.
*/ */
#include "PackageFilter.h" #include "PackageFilter.h"
#include "Logger.h"
#include "PackageFilterSpecification.h"
#include "PackageUtils.h" #include "PackageUtils.h"
@@ -52,6 +53,20 @@ AndFilter::AcceptsPackage(const PackageInfoRef& package) const
} }
class FalseFilter : public PackageFilter
{
public:
FalseFilter()
{
}
virtual bool AcceptsPackage(const PackageInfoRef& package) const
{
return false;
}
};
class StateFilter : public PackageFilter class StateFilter : public PackageFilter
{ {
public: public:
@@ -138,16 +153,14 @@ public:
}; };
class DevelopmentFilter : public PackageFilter class DevelopmentFilter : public PackageFilter {
{
public: public:
virtual bool AcceptsPackage(const PackageInfoRef& package) const virtual bool AcceptsPackage(const PackageInfoRef& package) const
{ {
if (!package.IsSet()) if (!package.IsSet())
return false; return false;
const BString& packageName = package->Name(); const BString& packageName = package->Name();
return packageName.EndsWith("_devel") return packageName.EndsWith("_devel") || packageName.EndsWith("_debuginfo");
|| packageName.EndsWith("_debuginfo");
} }
}; };
@@ -179,8 +192,7 @@ public:
// Every search term must be found in one of the package texts // Every search term must be found in one of the package texts
for (int32 i = fSearchTerms.CountStrings() - 1; i >= 0; i--) { for (int32 i = fSearchTerms.CountStrings() - 1; i >= 0; i--) {
const BString& term = fSearchTerms.StringAt(i); const BString& term = fSearchTerms.StringAt(i);
if (package->Name().FindFirst(term) < 0 if (package->Name().FindFirst(term) < 0 && !_AcceptsPackageFromPublisher(package, term)
&& !_AcceptsPackageFromPublisher(package, term)
&& !_AcceptsPackageFromLocalizedText(package, term)) { && !_AcceptsPackageFromLocalizedText(package, term)) {
return false; return false;
} }
@@ -199,15 +211,15 @@ public:
searchTerms.Append(" "); searchTerms.Append(" ");
searchTerms.Append(term); searchTerms.Append(term);
} }
return searchTerms; return searchTerms;
} }
private: private:
bool _TextContains(BString text, const BString& string) const bool _TextContains(BString text, const BString& string) const
{ {
int32 index = text.IFindFirst(string); int32 index = text.IFindFirst(string);
return index >= 0; return index >= 0;
} }
bool _AcceptsPackageFromPublisher(const PackageInfoRef& package, bool _AcceptsPackageFromPublisher(const PackageInfoRef& package,
const BString& searchTerm) const const BString& searchTerm) const
@@ -228,12 +240,12 @@ private:
return false; return false;
return _TextContains(localizedText->Title(), searchTerm) return _TextContains(localizedText->Title(), searchTerm)
|| _TextContains(localizedText->Summary(), searchTerm) || _TextContains(localizedText->Summary(), searchTerm)
|| _TextContains(localizedText->Description(), searchTerm); || _TextContains(localizedText->Description(), searchTerm);
} }
private: private:
BStringList fSearchTerms; BStringList fSearchTerms;
}; };
@@ -280,3 +292,43 @@ PackageFilterFactory::CreateDevelopmentFilter()
{ {
return PackageFilterRef(new DevelopmentFilter(), true); return PackageFilterRef(new DevelopmentFilter(), true);
} }
/*static*/ PackageFilterRef
PackageFilterFactory::CreateFalseFilter()
{
return PackageFilterRef(new FalseFilter(), true);
}
/*static*/ PackageFilterRef
PackageFilterFactory::CreateFilter(const PackageFilterSpecificationRef specification)
{
if (!specification.IsSet())
return CreateFalseFilter();
AndFilter* andFilter = new AndFilter();
if (!specification->SearchTerms().IsEmpty())
andFilter->AddFilter(CreateSearchTermsFilter(specification->SearchTerms()));
if (!specification->DepotName().IsEmpty())
andFilter->AddFilter(CreateDepotFilter(specification->DepotName()));
if (!specification->Category().IsEmpty())
andFilter->AddFilter(CreateCategoryFilter(specification->Category()));
if (!specification->ShowAvailablePackages())
andFilter->AddFilter(PackageFilterRef(new NotFilter(CreateStateFilter(NONE)), true));
if (!specification->ShowInstalledPackages())
andFilter->AddFilter(PackageFilterRef(new NotFilter(CreateStateFilter(ACTIVATED)), true));
if (!specification->ShowSourcePackages())
andFilter->AddFilter(PackageFilterRef(new NotFilter(CreateSourceFilter()), true));
if (!specification->ShowDevelopPackages())
andFilter->AddFilter(PackageFilterRef(new NotFilter(CreateDevelopmentFilter()), true));
return PackageFilterRef(andFilter, true);
}
@@ -1,6 +1,6 @@
/* /*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>. * Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2016-2023, Andrew Lindesay <[email protected]>. * Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
* *
* Note that this file included code earlier from `Model.cpp` and * Note that this file included code earlier from `Model.cpp` and
@@ -14,9 +14,13 @@
#include <Referenceable.h> #include <Referenceable.h>
#include <String.h> #include <String.h>
#include "PackageFilterSpecification.h"
#include "PackageInfo.h" #include "PackageInfo.h"
/*! See the class PackageFilterFactory for static factory methods that will
create concrete instances of this abstract superclass.
*/
class PackageFilter : public BReferenceable class PackageFilter : public BReferenceable
{ {
public: public:
@@ -61,6 +65,8 @@ public:
static PackageFilterRef CreateStateFilter(PackageState state); static PackageFilterRef CreateStateFilter(PackageState state);
static PackageFilterRef CreateSourceFilter(); static PackageFilterRef CreateSourceFilter();
static PackageFilterRef CreateDevelopmentFilter(); static PackageFilterRef CreateDevelopmentFilter();
static PackageFilterRef CreateFalseFilter();
static PackageFilterRef CreateFilter(const PackageFilterSpecificationRef specification);
}; };
@@ -1,180 +0,0 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "PackageFilterModel.h"
PackageFilterModel::PackageFilterModel()
:
fSearchTerms(""),
fDepotName(""),
fCategory(""),
fShowAvailablePackages(true),
fShowInstalledPackages(true),
fShowSourcePackages(false),
fShowDevelopPackages(false)
{
fFilter = _CreateFilter();
}
PackageFilterModel::~PackageFilterModel()
{
}
BString
PackageFilterModel::SearchTerms() const
{
return fSearchTerms;
}
BString
PackageFilterModel::DepotName() const
{
return fDepotName;
}
BString
PackageFilterModel::Category() const
{
return fCategory;
}
bool
PackageFilterModel::ShowAvailablePackages() const
{
return fShowAvailablePackages;
}
bool
PackageFilterModel::ShowInstalledPackages() const
{
return fShowInstalledPackages;
}
bool
PackageFilterModel::ShowSourcePackages() const
{
return fShowSourcePackages;
}
bool
PackageFilterModel::ShowDevelopPackages() const
{
return fShowDevelopPackages;
}
void
PackageFilterModel::SetSearchTerms(BString value)
{
if (fSearchTerms != value) {
fSearchTerms = value;
fFilter = _CreateFilter();
}
}
void
PackageFilterModel::SetDepotName(BString value)
{
if (fDepotName != value) {
fDepotName = value;
fFilter = _CreateFilter();
}
}
void
PackageFilterModel::SetCategory(BString value)
{
if (fCategory != value) {
fCategory = value;
fFilter = _CreateFilter();
}
}
void
PackageFilterModel::SetShowAvailablePackages(bool value)
{
if (fShowAvailablePackages != value) {
fShowAvailablePackages = value;
fFilter = _CreateFilter();
}
}
void
PackageFilterModel::SetShowInstalledPackages(bool value)
{
if (fShowInstalledPackages != value) {
fShowInstalledPackages = value;
fFilter = _CreateFilter();
}
}
void
PackageFilterModel::SetShowSourcePackages(bool value)
{
if (fShowSourcePackages != value) {
fShowSourcePackages = value;
fFilter = _CreateFilter();
}
}
void
PackageFilterModel::SetShowDevelopPackages(bool value)
{
if (fShowDevelopPackages != value) {
fShowDevelopPackages = value;
fFilter = _CreateFilter();
}
}
PackageFilterRef
PackageFilterModel::Filter()
{
return fFilter;
}
PackageFilterRef
PackageFilterModel::_CreateFilter() const
{
AndFilter* andFilter = new AndFilter();
if (!fSearchTerms.IsEmpty())
andFilter->AddFilter(PackageFilterFactory::CreateSearchTermsFilter(fSearchTerms));
if (!fDepotName.IsEmpty())
andFilter->AddFilter(PackageFilterFactory::CreateDepotFilter(fDepotName));
if (!fCategory.IsEmpty())
andFilter->AddFilter(PackageFilterFactory::CreateCategoryFilter(fCategory));
if (!fShowAvailablePackages) {
andFilter->AddFilter(
PackageFilterRef(new NotFilter(PackageFilterFactory::CreateStateFilter(NONE)), true));
}
if (!fShowInstalledPackages) {
andFilter->AddFilter(PackageFilterRef(
new NotFilter(PackageFilterFactory::CreateStateFilter(ACTIVATED)), true));
}
if (!fShowSourcePackages) {
andFilter->AddFilter(
PackageFilterRef(new NotFilter(PackageFilterFactory::CreateSourceFilter()), true));
}
if (!fShowDevelopPackages) {
andFilter->AddFilter(
PackageFilterRef(new NotFilter(PackageFilterFactory::CreateDevelopmentFilter()), true));
}
return PackageFilterRef(andFilter, true);
}
@@ -1,53 +0,0 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PACKAGE_FILTER_MODEL_H
#define PACKAGE_FILTER_MODEL_H
#include <String.h>
#include "PackageFilter.h"
class PackageFilterModel
{
public:
PackageFilterModel();
virtual ~PackageFilterModel();
BString SearchTerms() const;
BString DepotName() const;
BString Category() const;
bool ShowAvailablePackages() const;
bool ShowInstalledPackages() const;
bool ShowSourcePackages() const;
bool ShowDevelopPackages() const;
void SetSearchTerms(BString value);
void SetDepotName(BString value);
void SetCategory(BString value);
void SetShowAvailablePackages(bool value);
void SetShowInstalledPackages(bool value);
void SetShowSourcePackages(bool value);
void SetShowDevelopPackages(bool value);
PackageFilterRef Filter();
private:
PackageFilterRef _CreateFilter() const;
private:
BString fSearchTerms;
BString fDepotName;
BString fCategory;
bool fShowAvailablePackages;
bool fShowInstalledPackages;
bool fShowSourcePackages;
bool fShowDevelopPackages;
PackageFilterRef fFilter;
};
#endif // PACKAGE_FILTER_MODEL_H
@@ -0,0 +1,292 @@
/*
* Copyright 2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "PackageFilterSpecification.h"
PackageFilterSpecification::PackageFilterSpecification()
:
fSearchTerms(""),
fDepotName(""),
fCategory(""),
fShowAvailablePackages(true),
fShowInstalledPackages(true),
fShowSourcePackages(false),
fShowDevelopPackages(false)
{
}
PackageFilterSpecification::~PackageFilterSpecification()
{
}
bool
PackageFilterSpecification::operator==(const PackageFilterSpecification& other) const
{
return fSearchTerms == other.SearchTerms() && fDepotName == other.DepotName()
&& fCategory == other.Category() && fShowAvailablePackages == other.ShowAvailablePackages()
&& fShowInstalledPackages == other.ShowInstalledPackages()
&& fShowDevelopPackages == other.ShowDevelopPackages();
}
bool
PackageFilterSpecification::operator!=(const PackageFilterSpecification& other) const
{
return !(*this == other);
}
BString
PackageFilterSpecification::SearchTerms() const
{
return fSearchTerms;
}
BString
PackageFilterSpecification::DepotName() const
{
return fDepotName;
}
BString
PackageFilterSpecification::Category() const
{
return fCategory;
}
bool
PackageFilterSpecification::ShowAvailablePackages() const
{
return fShowAvailablePackages;
}
bool
PackageFilterSpecification::ShowInstalledPackages() const
{
return fShowInstalledPackages;
}
bool
PackageFilterSpecification::ShowSourcePackages() const
{
return fShowSourcePackages;
}
bool
PackageFilterSpecification::ShowDevelopPackages() const
{
return fShowDevelopPackages;
}
void
PackageFilterSpecification::SetSearchTerms(BString value)
{
fSearchTerms = value;
}
void
PackageFilterSpecification::SetDepotName(BString value)
{
fDepotName = value;
}
void
PackageFilterSpecification::SetCategory(BString value)
{
fCategory = value;
}
void
PackageFilterSpecification::SetShowAvailablePackages(bool value)
{
fShowAvailablePackages = value;
}
void
PackageFilterSpecification::SetShowInstalledPackages(bool value)
{
fShowInstalledPackages = value;
}
void
PackageFilterSpecification::SetShowSourcePackages(bool value)
{
fShowSourcePackages = value;
}
void
PackageFilterSpecification::SetShowDevelopPackages(bool value)
{
fShowDevelopPackages = value;
}
// #pragma mark - PackageClassificationInfoBuilder
PackageFilterSpecificationBuilder::PackageFilterSpecificationBuilder()
:
fSearchTerms(""),
fDepotName(""),
fCategory(""),
fShowAvailablePackages(true),
fShowInstalledPackages(true),
fShowSourcePackages(false),
fShowDevelopPackages(false)
{
}
PackageFilterSpecificationBuilder::PackageFilterSpecificationBuilder(
const PackageFilterSpecificationRef& value)
:
fSource(value),
fSearchTerms(""),
fDepotName(""),
fCategory(""),
fShowAvailablePackages(true),
fShowInstalledPackages(true),
fShowSourcePackages(false),
fShowDevelopPackages(false)
{
}
PackageFilterSpecificationBuilder::~PackageFilterSpecificationBuilder()
{
}
void
PackageFilterSpecificationBuilder::_InitFromSource()
{
if (fSource.IsSet()) {
_Init(fSource);
fSource.Unset();
}
}
void
PackageFilterSpecificationBuilder::_Init(const PackageFilterSpecification* value)
{
fSearchTerms = value->SearchTerms();
fDepotName = value->DepotName();
fCategory = value->Category();
fShowAvailablePackages = value->ShowAvailablePackages();
fShowInstalledPackages = value->ShowInstalledPackages();
fShowSourcePackages = value->ShowSourcePackages();
fShowDevelopPackages = value->ShowDevelopPackages();
}
PackageFilterSpecificationRef
PackageFilterSpecificationBuilder::BuildRef()
{
if (fSource.IsSet())
return fSource;
PackageFilterSpecification* info = new PackageFilterSpecification();
info->SetSearchTerms(fSearchTerms);
info->SetDepotName(fDepotName);
info->SetCategory(fCategory);
info->SetShowAvailablePackages(fShowAvailablePackages);
info->SetShowInstalledPackages(fShowInstalledPackages);
info->SetShowSourcePackages(fShowSourcePackages);
info->SetShowDevelopPackages(fShowDevelopPackages);
return PackageFilterSpecificationRef(info, true);
}
PackageFilterSpecificationBuilder
PackageFilterSpecificationBuilder::WithSearchTerms(BString value)
{
if (!fSource.IsSet() || fSource->SearchTerms() != value) {
_InitFromSource();
fSearchTerms = value;
}
return *this;
}
PackageFilterSpecificationBuilder
PackageFilterSpecificationBuilder::WithDepotName(BString value)
{
if (!fSource.IsSet() || fSource->DepotName() != value) {
_InitFromSource();
fDepotName = value;
}
return *this;
}
PackageFilterSpecificationBuilder
PackageFilterSpecificationBuilder::WithCategory(BString value)
{
if (!fSource.IsSet() || fSource->Category() != value) {
_InitFromSource();
fCategory = value;
}
return *this;
}
PackageFilterSpecificationBuilder
PackageFilterSpecificationBuilder::WithShowAvailablePackages(bool value)
{
if (!fSource.IsSet() || fSource->ShowAvailablePackages() != value) {
_InitFromSource();
fShowAvailablePackages = value;
}
return *this;
}
PackageFilterSpecificationBuilder
PackageFilterSpecificationBuilder::WithShowInstalledPackages(bool value)
{
if (!fSource.IsSet() || fSource->ShowInstalledPackages() != value) {
_InitFromSource();
fShowInstalledPackages = value;
}
return *this;
}
PackageFilterSpecificationBuilder
PackageFilterSpecificationBuilder::WithShowSourcePackages(bool value)
{
if (!fSource.IsSet() || fSource->ShowSourcePackages() != value) {
_InitFromSource();
fShowSourcePackages = value;
}
return *this;
}
PackageFilterSpecificationBuilder
PackageFilterSpecificationBuilder::WithShowDevelopPackages(bool value)
{
if (!fSource.IsSet() || fSource->ShowDevelopPackages() != value) {
_InitFromSource();
fShowDevelopPackages = value;
}
return *this;
}
@@ -0,0 +1,100 @@
/*
* Copyright 2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PACKAGE_FILTER_SPECIFICATION_H
#define PACKAGE_FILTER_SPECIFICATION_H
#include <Referenceable.h>
#include <String.h>
/*! Instances of this class should not be created directly; instead use the
PackageFilterSpecificationBuilder class as a builder-constructor.
*/
class PackageFilterSpecification : public BReferenceable
{
friend class PackageFilterSpecificationBuilder;
public:
PackageFilterSpecification();
virtual ~PackageFilterSpecification();
bool operator==(const PackageFilterSpecification& other) const;
bool operator!=(const PackageFilterSpecification& other) const;
BString SearchTerms() const;
BString DepotName() const;
BString Category() const;
bool ShowAvailablePackages() const;
bool ShowInstalledPackages() const;
bool ShowSourcePackages() const;
bool ShowDevelopPackages() const;
private:
void SetSearchTerms(BString value);
void SetDepotName(BString value);
void SetCategory(BString value);
void SetShowAvailablePackages(bool value);
void SetShowInstalledPackages(bool value);
void SetShowSourcePackages(bool value);
void SetShowDevelopPackages(bool value);
private:
BString fSearchTerms;
BString fDepotName;
BString fCategory;
bool fShowAvailablePackages;
bool fShowInstalledPackages;
bool fShowSourcePackages;
bool fShowDevelopPackages;
};
typedef BReference<PackageFilterSpecification> PackageFilterSpecificationRef;
class PackageFilterSpecificationBuilder
{
public:
PackageFilterSpecificationBuilder();
PackageFilterSpecificationBuilder(
const PackageFilterSpecificationRef& other);
virtual ~PackageFilterSpecificationBuilder();
PackageFilterSpecificationRef
BuildRef();
PackageFilterSpecificationBuilder
WithSearchTerms(BString value);
PackageFilterSpecificationBuilder
WithDepotName(BString value);
PackageFilterSpecificationBuilder
WithCategory(BString value);
PackageFilterSpecificationBuilder
WithShowAvailablePackages(bool value);
PackageFilterSpecificationBuilder
WithShowInstalledPackages(bool value);
PackageFilterSpecificationBuilder
WithShowSourcePackages(bool value);
PackageFilterSpecificationBuilder
WithShowDevelopPackages(bool value);
private:
void _InitFromSource();
void _Init(const PackageFilterSpecification* value);
private:
PackageFilterSpecificationRef
fSource;
BString fSearchTerms;
BString fDepotName;
BString fCategory;
bool fShowAvailablePackages;
bool fShowInstalledPackages;
bool fShowSourcePackages;
bool fShowDevelopPackages;
};
#endif // PACKAGE_FILTER_SPECIFICATION_H
+139 -150
View File
@@ -1,7 +1,7 @@
/* /*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>. * Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2013, Rene Gollent <[email protected]>. * Copyright 2013, Rene Gollent <[email protected]>.
* Copyright 2016-2024, Andrew Lindesay <[email protected]>. * Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
@@ -15,6 +15,7 @@
#include "HaikuDepotConstants.h" #include "HaikuDepotConstants.h"
#include "Logger.h" #include "Logger.h"
#include "PackageInfoListener.h"
// #pragma mark - PackageInfo // #pragma mark - PackageInfo
@@ -29,66 +30,11 @@ PackageInfo::PackageInfo()
fClassificationInfo(), fClassificationInfo(),
fScreenshotInfo(), fScreenshotInfo(),
fUserRatingInfo(), fUserRatingInfo(),
fLocalInfo(), fLocalInfo()
fListeners(),
fIsCollatingChanges(false),
fCollatedChanges(0)
{ {
} }
PackageInfo::PackageInfo(const BPackageInfo& info)
:
fName(info.Name()),
fClassificationInfo(),
fScreenshotInfo(),
fUserRatingInfo(),
fListeners(),
fIsCollatingChanges(false),
fCollatedChanges(0)
{
// TODO; factor this material out.
BString publisherURL;
if (info.URLList().CountStrings() > 0)
publisherURL = info.URLList().StringAt(0);
BString publisherName = info.Vendor();
const BStringList& copyrightList = info.CopyrightList();
if (!copyrightList.IsEmpty()) {
publisherName = "";
for (int32 i = 0; i < copyrightList.CountStrings(); i++) {
if (!publisherName.IsEmpty())
publisherName << ", ";
publisherName << copyrightList.StringAt(i);
}
}
if (!publisherName.IsEmpty())
publisherName.Prepend("© ");
fCoreInfo = PackageCoreInfoRef(new PackageCoreInfo(), true);
fCoreInfo->SetArchitecture(info.ArchitectureName());
fCoreInfo->SetVersion(new PackageVersion(info.Version()));
fCoreInfo->SetPublisher(
PackagePublisherInfoRef(new PackagePublisherInfo(publisherName, publisherURL), true));
fLocalizedText = PackageLocalizedTextRef(new PackageLocalizedText(), true);
fLocalizedText->SetTitle(info.Name());
fLocalizedText->SetSummary(info.Summary());
fLocalizedText->SetDescription(info.Description());
// TODO: Retrieve local file size
fLocalInfo = PackageLocalInfoRef(new PackageLocalInfo(), true);
fLocalInfo->SetFlags(info.Flags());
fLocalInfo->SetFileName(info.FileName());
}
PackageInfo::PackageInfo(const PackageInfo& other) PackageInfo::PackageInfo(const PackageInfo& other)
: :
fName(other.fName), fName(other.fName),
@@ -98,30 +44,11 @@ PackageInfo::PackageInfo(const PackageInfo& other)
fClassificationInfo(other.fClassificationInfo), fClassificationInfo(other.fClassificationInfo),
fScreenshotInfo(other.fScreenshotInfo), fScreenshotInfo(other.fScreenshotInfo),
fUserRatingInfo(other.fUserRatingInfo), fUserRatingInfo(other.fUserRatingInfo),
fLocalInfo(other.fLocalInfo), fLocalInfo(other.fLocalInfo)
fListeners(),
fIsCollatingChanges(false),
fCollatedChanges(0)
{ {
} }
PackageInfo&
PackageInfo::operator=(const PackageInfo& other)
{
fName = other.fName;
fCoreInfo = other.fCoreInfo;
fLocalizedText = other.fLocalizedText;
fClassificationInfo = other.fClassificationInfo;
fScreenshotInfo = other.fScreenshotInfo;
fUserRatingInfo = other.fUserRatingInfo;
fLocalInfo = other.fLocalInfo;
return *this;
}
bool bool
PackageInfo::operator==(const PackageInfo& other) const PackageInfo::operator==(const PackageInfo& other) const
{ {
@@ -142,8 +69,43 @@ PackageInfo::operator!=(const PackageInfo& other) const
} }
const BString&
PackageInfo::Name() const
{
return fName;
}
const PackageCoreInfoRef
PackageInfo::CoreInfo() const
{
return fCoreInfo;
}
const PackageClassificationInfoRef
PackageInfo::PackageClassificationInfo() const
{
return fClassificationInfo;
}
const PackageScreenshotInfoRef
PackageInfo::ScreenshotInfo() const
{
return fScreenshotInfo;
}
void
PackageInfo::SetName(const BString& value)
{
fName = value;
}
uint32 uint32
PackageInfo::DiffMask(const PackageInfo& other) const PackageInfo::ChangeMask(const PackageInfo& other) const
{ {
uint32 result = 0; uint32 result = 0;
@@ -167,14 +129,11 @@ PackageInfo::DiffMask(const PackageInfo& other) const
void void
PackageInfo::SetCoreInfo(PackageCoreInfoRef value) PackageInfo::SetCoreInfo(PackageCoreInfoRef value)
{ {
if (value != fCoreInfo) { fCoreInfo = value;
fCoreInfo = value;
_NotifyListeners(PKG_CHANGED_CORE_INFO);
}
} }
PackageLocalizedTextRef const PackageLocalizedTextRef
PackageInfo::LocalizedText() const PackageInfo::LocalizedText() const
{ {
return fLocalizedText; return fLocalizedText;
@@ -184,16 +143,11 @@ PackageInfo::LocalizedText() const
void void
PackageInfo::SetLocalizedText(PackageLocalizedTextRef value) PackageInfo::SetLocalizedText(PackageLocalizedTextRef value)
{ {
if (fLocalizedText != value) { fLocalizedText = value;
fLocalizedText = value;
_NotifyListeners(PKG_CHANGED_LOCALIZED_TEXT);
// TODO; separate out these later - they are bundled for now to keep the existing
// logic working.
}
} }
UserRatingInfoRef const PackageUserRatingInfoRef
PackageInfo::UserRatingInfo() const PackageInfo::UserRatingInfo() const
{ {
return fUserRatingInfo; return fUserRatingInfo;
@@ -201,16 +155,13 @@ PackageInfo::UserRatingInfo() const
void void
PackageInfo::SetUserRatingInfo(UserRatingInfoRef value) PackageInfo::SetUserRatingInfo(PackageUserRatingInfoRef& value)
{ {
if (fUserRatingInfo != value) { fUserRatingInfo = value;
fUserRatingInfo = value;
_NotifyListeners(PKG_CHANGED_RATINGS);
}
} }
PackageLocalInfoRef const PackageLocalInfoRef
PackageInfo::LocalInfo() const PackageInfo::LocalInfo() const
{ {
return fLocalInfo; return fLocalInfo;
@@ -220,99 +171,137 @@ PackageInfo::LocalInfo() const
void void
PackageInfo::SetLocalInfo(PackageLocalInfoRef& localInfo) PackageInfo::SetLocalInfo(PackageLocalInfoRef& localInfo)
{ {
if (fLocalInfo != localInfo) { fLocalInfo = localInfo;
fLocalInfo = localInfo;
_NotifyListeners(PKG_CHANGED_LOCAL_INFO);
}
} }
void void
PackageInfo::SetPackageClassificationInfo(PackageClassificationInfoRef value) PackageInfo::SetPackageClassificationInfo(PackageClassificationInfoRef value)
{ {
if (value != fClassificationInfo) { fClassificationInfo = value;
fClassificationInfo = value;
_NotifyListeners(PKG_CHANGED_CLASSIFICATION);
}
} }
void void
PackageInfo::SetScreenshotInfo(PackageScreenshotInfoRef value) PackageInfo::SetScreenshotInfo(PackageScreenshotInfoRef value)
{ {
if (value != fScreenshotInfo) { fScreenshotInfo = value;
fScreenshotInfo = value;
_NotifyListeners(PKG_CHANGED_SCREENSHOTS);
}
} }
bool // #pragma mark - PackageLocalInfoBuilder
PackageInfo::AddListener(const PackageInfoListenerRef& listener)
PackageInfoBuilder::PackageInfoBuilder(const BString& name)
:
fName(name),
fCoreInfo(),
fLocalizedText(),
fClassificationInfo(),
fScreenshotInfo(),
fUserRatingInfo(),
fLocalInfo()
{
}
PackageInfoBuilder::PackageInfoBuilder(const PackageInfoRef& value)
:
fName(),
fCoreInfo(),
fLocalizedText(),
fClassificationInfo(),
fScreenshotInfo(),
fUserRatingInfo(),
fLocalInfo()
{
if (value.IsSet())
_Init(value.Get());
}
PackageInfoBuilder::PackageInfoBuilder(const PackageInfo& value)
{
_Init(&value);
}
PackageInfoBuilder::~PackageInfoBuilder()
{ {
fListeners.push_back(listener);
return true;
} }
void void
PackageInfo::RemoveListener(const PackageInfoListenerRef& listener) PackageInfoBuilder::_Init(const PackageInfo* value)
{ {
fListeners.erase(std::remove(fListeners.begin(), fListeners.end(), fName = value->Name();
listener), fListeners.end()); fCoreInfo = value->CoreInfo();
fLocalizedText = value->LocalizedText();
fClassificationInfo = value->PackageClassificationInfo();
fScreenshotInfo = value->ScreenshotInfo();
fUserRatingInfo = value->UserRatingInfo();
fLocalInfo = value->LocalInfo();
} }
void PackageInfoRef
PackageInfo::NotifyChangedIcon() PackageInfoBuilder::BuildRef()
{ {
_NotifyListeners(PKG_CHANGED_ICON); PackageInfo* info = new PackageInfo();
info->SetName(fName);
info->SetCoreInfo(fCoreInfo);
info->SetLocalizedText(fLocalizedText);
info->SetPackageClassificationInfo(fClassificationInfo);
info->SetScreenshotInfo(fScreenshotInfo);
info->SetUserRatingInfo(fUserRatingInfo);
info->SetLocalInfo(fLocalInfo);
return PackageInfoRef(info, true);
} }
void PackageInfoBuilder&
PackageInfo::StartCollatingChanges() PackageInfoBuilder::WithCoreInfo(PackageCoreInfoRef value)
{ {
fIsCollatingChanges = true; fCoreInfo = value;
fCollatedChanges = 0; return *this;
} }
void PackageInfoBuilder&
PackageInfo::EndCollatingChanges() PackageInfoBuilder::WithLocalizedText(PackageLocalizedTextRef value)
{ {
if (fIsCollatingChanges && fCollatedChanges != 0) fLocalizedText = value;
_NotifyListenersImmediate(fCollatedChanges); return *this;
fIsCollatingChanges = false;
fCollatedChanges = 0;
} }
void PackageInfoBuilder&
PackageInfo::_NotifyListeners(uint32 changes) PackageInfoBuilder::WithPackageClassificationInfo(PackageClassificationInfoRef value)
{ {
if (fIsCollatingChanges) fClassificationInfo = value;
fCollatedChanges |= changes; return *this;
else
_NotifyListenersImmediate(changes);
} }
void PackageInfoBuilder&
PackageInfo::_NotifyListenersImmediate(uint32 changes) PackageInfoBuilder::WithUserRatingInfo(PackageUserRatingInfoRef value)
{ {
if (fListeners.empty()) fUserRatingInfo = value;
return; return *this;
}
// Clone list to avoid listeners detaching themselves in notifications
// to screw up the list while iterating it.
std::vector<PackageInfoListenerRef> listeners(fListeners); PackageInfoBuilder&
PackageInfoEvent event(PackageInfoRef(this), changes); PackageInfoBuilder::WithLocalInfo(PackageLocalInfoRef value)
{
std::vector<PackageInfoListenerRef>::iterator it; fLocalInfo = value;
for (it = listeners.begin(); it != listeners.end(); it++) { return *this;
const PackageInfoListenerRef listener = *it; }
if (listener.IsSet())
listener->PackageChanged(event);
} PackageInfoBuilder&
PackageInfoBuilder::WithScreenshotInfo(PackageScreenshotInfoRef value)
{
fScreenshotInfo = value;
return *this;
} }
+75 -52
View File
@@ -1,6 +1,6 @@
/* /*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>. * Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2016-2024, Andrew Lindesay <[email protected]>. * Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef PACKAGE_INFO_H #ifndef PACKAGE_INFO_H
@@ -8,76 +8,63 @@
#include <Referenceable.h> #include <Referenceable.h>
#include <package/PackageInfo.h>
#include "Language.h" #include "Language.h"
#include "List.h" #include "List.h"
#include "PackageClassificationInfo.h" #include "PackageClassificationInfo.h"
#include "PackageCoreInfo.h" #include "PackageCoreInfo.h"
#include "PackageInfoListener.h"
#include "PackageLocalInfo.h" #include "PackageLocalInfo.h"
#include "PackageLocalizedText.h" #include "PackageLocalizedText.h"
#include "PackagePublisherInfo.h" #include "PackagePublisherInfo.h"
#include "PackageScreenshotInfo.h" #include "PackageScreenshotInfo.h"
#include "PackageUserRatingInfo.h"
#include "ScreenshotInfo.h" #include "ScreenshotInfo.h"
#include "UserRatingInfo.h"
using BPackageKit::BPackageInfo; using BPackageKit::BPackageInfo;
class PackageInfoBuilder;
/*! Instances of this class should not be created directly; instead use the
PackageInfoBuilder class as a builder-constructor.
*/
class PackageInfo : public BReferenceable { class PackageInfo : public BReferenceable {
friend class PackageInfoBuilder;
public: public:
PackageInfo(); PackageInfo();
PackageInfo(const BPackageInfo& info);
PackageInfo(const PackageInfo& other); PackageInfo(const PackageInfo& other);
PackageInfo& operator=(const PackageInfo& other);
bool operator==(const PackageInfo& other) const; bool operator==(const PackageInfo& other) const;
bool operator!=(const PackageInfo& other) const; bool operator!=(const PackageInfo& other) const;
uint32 DiffMask(const PackageInfo& other) const; uint32 ChangeMask(const PackageInfo& other) const;
const BString& Name() const const BString& Name() const;
{ return fName; } const PackageCoreInfoRef
CoreInfo() const;
PackageCoreInfoRef CoreInfo() const const PackageLocalizedTextRef
{ return fCoreInfo; }
void SetCoreInfo(PackageCoreInfoRef value);
PackageLocalizedTextRef
LocalizedText() const; LocalizedText() const;
void SetLocalizedText(PackageLocalizedTextRef value); const PackageClassificationInfoRef
PackageClassificationInfo() const;
void SetPackageClassificationInfo( const PackageUserRatingInfoRef
PackageClassificationInfoRef value); UserRatingInfo() const;
PackageClassificationInfoRef const PackageLocalInfoRef
PackageClassificationInfo() const LocalInfo() const;
{ return fClassificationInfo; } const PackageScreenshotInfoRef
ScreenshotInfo() const;
UserRatingInfoRef UserRatingInfo() const;
void SetUserRatingInfo(UserRatingInfoRef userRatingInfo);
PackageLocalInfoRef LocalInfo() const;
void SetLocalInfo(PackageLocalInfoRef& localInfo);
PackageScreenshotInfoRef
ScreenshotInfo() const
{ return fScreenshotInfo; }
void SetScreenshotInfo(PackageScreenshotInfoRef value);
bool AddListener(
const PackageInfoListenerRef& listener);
void RemoveListener(
const PackageInfoListenerRef& listener);
void StartCollatingChanges();
void EndCollatingChanges();
void NotifyChangedIcon();
private: private:
void _NotifyListeners(uint32 changes);
void _NotifyListenersImmediate(uint32 changes); void SetName(const BString& value);
void SetCoreInfo(PackageCoreInfoRef value);
void SetLocalizedText(PackageLocalizedTextRef value);
void SetPackageClassificationInfo(PackageClassificationInfoRef value);
void SetUserRatingInfo(PackageUserRatingInfoRef& userRatingInfo);
void SetLocalInfo(PackageLocalInfoRef& localInfo);
void SetScreenshotInfo(PackageScreenshotInfoRef value);
private: private:
BString fName; BString fName;
@@ -89,19 +76,55 @@ private:
fClassificationInfo; fClassificationInfo;
PackageScreenshotInfoRef PackageScreenshotInfoRef
fScreenshotInfo; fScreenshotInfo;
UserRatingInfoRef fUserRatingInfo; PackageUserRatingInfoRef
fUserRatingInfo;
PackageLocalInfoRef fLocalInfo; PackageLocalInfoRef fLocalInfo;
std::vector<PackageInfoListenerRef>
fListeners;
bool fIsCollatingChanges;
uint32 fCollatedChanges;
}; };
typedef BReference<PackageInfo> PackageInfoRef; typedef BReference<PackageInfo> PackageInfoRef;
class PackageInfoBuilder
{
public:
PackageInfoBuilder(const BString& name);
PackageInfoBuilder(const PackageInfoRef& value);
PackageInfoBuilder(const PackageInfo& value);
virtual ~PackageInfoBuilder();
PackageInfoRef BuildRef();
PackageInfoBuilder&
WithCoreInfo(PackageCoreInfoRef value);
PackageInfoBuilder&
WithLocalizedText(PackageLocalizedTextRef value);
PackageInfoBuilder&
WithPackageClassificationInfo(PackageClassificationInfoRef value);
PackageInfoBuilder&
WithUserRatingInfo(PackageUserRatingInfoRef userRatingInfo);
PackageInfoBuilder&
WithLocalInfo(PackageLocalInfoRef localInfo);
PackageInfoBuilder&
WithScreenshotInfo(PackageScreenshotInfoRef value);
private:
void _Init(const PackageInfo* value);
private:
BString fName;
PackageCoreInfoRef fCoreInfo;
PackageLocalizedTextRef
fLocalizedText;
PackageClassificationInfoRef
fClassificationInfo;
PackageScreenshotInfoRef
fScreenshotInfo;
PackageUserRatingInfoRef
fUserRatingInfo;
PackageLocalInfoRef fLocalInfo;
};
#endif // PACKAGE_INFO_H #endif // PACKAGE_INFO_H
@@ -1,5 +1,5 @@
/* /*
* Copyright 2024, Andrew Lindesay <[email protected]>. * Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
@@ -49,23 +49,6 @@ PackageLocalInfo::~PackageLocalInfo()
} }
PackageLocalInfo&
PackageLocalInfo::operator=(const PackageLocalInfo& other)
{
fState = other.fState;
fInstallationLocations = other.fInstallationLocations;
fDownloadProgress = other.fDownloadProgress;
fFlags = other.fFlags;
fSystemDependency = other.fSystemDependency;
fLocalFilePath = other.fLocalFilePath;
fFileName = other.fFileName;
fSize = other.fSize;
fViewed = other.fViewed;
return *this;
}
bool bool
PackageLocalInfo::operator==(const PackageLocalInfo& other) const PackageLocalInfo::operator==(const PackageLocalInfo& other) const
{ {
@@ -88,6 +71,69 @@ PackageLocalInfo::operator!=(const PackageLocalInfo& other) const
} }
bool
PackageLocalInfo::Viewed() const
{
return fViewed;
}
const BString&
PackageLocalInfo::LocalFilePath() const
{
return fLocalFilePath;
}
const BString&
PackageLocalInfo::FileName() const
{
return fFileName;
}
off_t
PackageLocalInfo::Size() const
{
return fSize;
}
int32
PackageLocalInfo::Flags() const
{
return fFlags;
}
bool
PackageLocalInfo::IsSystemDependency() const
{
return fSystemDependency;
}
PackageState
PackageLocalInfo::State() const
{
return fState;
}
const PackageInstallationLocationSet&
PackageLocalInfo::InstallationLocations() const
{
return fInstallationLocations;
}
float
PackageLocalInfo::DownloadProgress() const
{
return fDownloadProgress;
}
bool bool
PackageLocalInfo::IsSystemPackage() const PackageLocalInfo::IsSystemPackage() const
{ {
@@ -122,10 +168,17 @@ PackageLocalInfo::AddInstallationLocation(int32 location)
} }
void bool
PackageLocalInfo::ClearInstallationLocations() PackageLocalInfo::HasInstallationLocation(int32 location) const
{ {
fInstallationLocations.clear(); return fInstallationLocations.find(location) != fInstallationLocations.end();
}
int32
PackageLocalInfo::CountInstallationLocations() const
{
return static_cast<int32>(fInstallationLocations.size());
} }
@@ -177,3 +230,211 @@ PackageLocalInfo::SetFileName(const BString& value)
{ {
fFileName = value; fFileName = value;
} }
// #pragma mark - PackageLocalInfoBuilder
PackageLocalInfoBuilder::PackageLocalInfoBuilder()
:
fViewed(false),
fLocalFilePath(),
fFileName(),
fSize(0),
fFlags(0),
fSystemDependency(false),
fState(NONE),
fInstallationLocations(),
fDownloadProgress(0.0)
{
}
PackageLocalInfoBuilder::PackageLocalInfoBuilder(const PackageLocalInfoRef& value)
:
fViewed(false),
fLocalFilePath(),
fFileName(),
fSize(0),
fFlags(0),
fSystemDependency(false),
fState(NONE),
fInstallationLocations(),
fDownloadProgress(0.0)
{
fSource = value;
}
PackageLocalInfoBuilder::~PackageLocalInfoBuilder()
{
}
void
PackageLocalInfoBuilder::_InitFromSource()
{
if (fSource.IsSet()) {
_Init(fSource);
fSource.Unset();
}
}
void
PackageLocalInfoBuilder::_Init(const PackageLocalInfo* value)
{
fViewed = value->Viewed();
fLocalFilePath = value->LocalFilePath();
fFileName = value->FileName();
fSize = value->Size();
fFlags = value->Flags();
fSystemDependency = value->IsSystemDependency();
fState = value->State();
fDownloadProgress = value->DownloadProgress();
PackageInstallationLocationSet valueLocations = value->InstallationLocations();
PackageInstallationLocationSet::const_iterator it;
for (it = valueLocations.begin(); it != valueLocations.end(); it++)
fInstallationLocations.insert(*it);
}
PackageLocalInfoRef
PackageLocalInfoBuilder::BuildRef()
{
if (fSource.IsSet())
return fSource;
PackageLocalInfo* localInfo = new PackageLocalInfo();
if (fViewed)
localInfo->SetViewed();
localInfo->SetLocalFilePath(fLocalFilePath);
localInfo->SetFileName(fFileName);
localInfo->SetSize(fSize);
localInfo->SetFlags(fFlags);
localInfo->SetSystemDependency(fSystemDependency);
localInfo->SetState(fState);
if (fState == DOWNLOADING)
localInfo->SetDownloadProgress(fDownloadProgress);
PackageInstallationLocationSet::const_iterator it;
for (it = fInstallationLocations.begin(); it != fInstallationLocations.end(); it++)
localInfo->AddInstallationLocation(*it);
return PackageLocalInfoRef(localInfo, true);
}
PackageLocalInfoBuilder&
PackageLocalInfoBuilder::WithViewed()
{
if (!fSource.IsSet() || fSource->Viewed()) {
_InitFromSource();
fViewed = true;
}
return *this;
}
PackageLocalInfoBuilder&
PackageLocalInfoBuilder::WithLocalFilePath(const char* path)
{
if (!fSource.IsSet() || fSource->LocalFilePath() != path) {
_InitFromSource();
fLocalFilePath.SetTo(path);
}
return *this;
}
PackageLocalInfoBuilder&
PackageLocalInfoBuilder::WithFileName(const BString& value)
{
if (!fSource.IsSet() || fSource->FileName() != value) {
_InitFromSource();
fFileName.SetTo(value);
}
return *this;
}
PackageLocalInfoBuilder&
PackageLocalInfoBuilder::WithSize(off_t size)
{
if (!fSource.IsSet() || fSource->Size() != size) {
_InitFromSource();
fSize = size;
}
return *this;
}
PackageLocalInfoBuilder&
PackageLocalInfoBuilder::WithFlags(int32 value)
{
if (!fSource.IsSet() || fSource->Flags() != value) {
_InitFromSource();
fFlags = value;
}
return *this;
}
PackageLocalInfoBuilder&
PackageLocalInfoBuilder::WithSystemDependency(bool value)
{
if (!fSource.IsSet() || fSource->IsSystemDependency() != value) {
_InitFromSource();
fSystemDependency = value;
}
return *this;
}
PackageLocalInfoBuilder&
PackageLocalInfoBuilder::WithState(PackageState value)
{
if (!fSource.IsSet() || fSource->State() != value) {
_InitFromSource();
fState = value;
}
return *this;
}
PackageLocalInfoBuilder&
PackageLocalInfoBuilder::WithDownloadProgress(float value)
{
if (!fSource.IsSet() || fSource->DownloadProgress() != value) {
_InitFromSource();
fDownloadProgress = value;
}
return *this;
}
PackageLocalInfoBuilder&
PackageLocalInfoBuilder::AddInstallationLocation(int32 value)
{
if (!fSource.IsSet() || !fSource->HasInstallationLocation(value)) {
_InitFromSource();
fInstallationLocations.insert(value);
}
return *this;
}
PackageLocalInfoBuilder&
PackageLocalInfoBuilder::ClearInstallationLocations()
{
if (!fSource.IsSet() || fSource->CountInstallationLocations() != 0) {
_InitFromSource();
fInstallationLocations.clear();
}
return *this;
}
@@ -1,5 +1,5 @@
/* /*
* Copyright 2024, Andrew Lindesay <[email protected]>. * Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef PACKAGE_LOCAL_INFO_H #ifndef PACKAGE_LOCAL_INFO_H
@@ -27,58 +27,49 @@ enum PackageState {
}; };
class PackageLocalInfoBuilder;
/*! Instances of this class should not be created directly; instead use the
PackageLocalInfoBuilder class as a builder-constructor.
*/
class PackageLocalInfo : public BReferenceable class PackageLocalInfo : public BReferenceable
{ {
friend class PackageLocalInfoBuilder;
public: public:
PackageLocalInfo(); PackageLocalInfo();
PackageLocalInfo(const PackageLocalInfo& other); PackageLocalInfo(const PackageLocalInfo& other);
virtual ~PackageLocalInfo(); virtual ~PackageLocalInfo();
PackageLocalInfo& operator=(const PackageLocalInfo& other);
bool operator==(const PackageLocalInfo& other) const; bool operator==(const PackageLocalInfo& other) const;
bool operator!=(const PackageLocalInfo& other) const; bool operator!=(const PackageLocalInfo& other) const;
bool IsLocalFile() const; bool IsLocalFile() const;
bool Viewed() const;
void SetViewed(); const BString& LocalFilePath() const;
bool Viewed() const const BString& FileName() const;
{ return fViewed; } off_t Size() const;
int32 Flags() const;
void SetLocalFilePath(const char* path);
const BString& LocalFilePath() const
{ return fLocalFilePath; }
void SetFileName(const BString& value);
const BString& FileName() const
{ return fFileName; }
void SetSize(off_t size);
off_t Size() const
{ return fSize; }
void SetFlags(int32 value);
int32 Flags() const
{ return fFlags; }
bool IsSystemPackage() const; bool IsSystemPackage() const;
bool IsSystemDependency() const;
void SetSystemDependency(bool isDependency); PackageState State() const;
bool IsSystemDependency() const bool HasInstallationLocation(int32 location) const;
{ return fSystemDependency; } int32 CountInstallationLocations() const;
void SetState(PackageState state);
PackageState State() const
{ return fState; }
void AddInstallationLocation(int32 location);
const PackageInstallationLocationSet& const PackageInstallationLocationSet&
InstallationLocations() const InstallationLocations() const;
{ return fInstallationLocations; } float DownloadProgress() const;
void ClearInstallationLocations();
private:
void SetViewed();
void SetLocalFilePath(const char* path);
void SetFileName(const BString& value);
void SetSize(off_t size);
void SetFlags(int32 value);
void SetSystemDependency(bool isDependency);
void SetState(PackageState state);
void AddInstallationLocation(int32 location);
void SetDownloadProgress(float progress); void SetDownloadProgress(float progress);
float DownloadProgress() const
{ return fDownloadProgress; }
private: private:
bool fViewed; bool fViewed;
@@ -97,4 +88,56 @@ private:
typedef BReference<PackageLocalInfo> PackageLocalInfoRef; typedef BReference<PackageLocalInfo> PackageLocalInfoRef;
class PackageLocalInfoBuilder
{
public:
PackageLocalInfoBuilder();
PackageLocalInfoBuilder(const PackageLocalInfoRef& value);
virtual ~PackageLocalInfoBuilder();
PackageLocalInfoRef BuildRef();
PackageLocalInfoBuilder&
WithViewed();
PackageLocalInfoBuilder&
WithLocalFilePath(const char* path);
PackageLocalInfoBuilder&
WithFileName(const BString& value);
PackageLocalInfoBuilder&
WithSize(off_t size);
PackageLocalInfoBuilder&
WithFlags(int32 value);
PackageLocalInfoBuilder&
WithSystemDependency(bool isDependency);
PackageLocalInfoBuilder&
WithState(PackageState state);
PackageLocalInfoBuilder&
WithDownloadProgress(float progress);
PackageLocalInfoBuilder&
AddInstallationLocation(int32 location);
PackageLocalInfoBuilder&
ClearInstallationLocations();
private:
void _InitFromSource();
void _Init(const PackageLocalInfo* value);
private:
PackageLocalInfoRef fSource;
bool fViewed;
BString fLocalFilePath;
BString fFileName;
off_t fSize;
int32 fFlags;
bool fSystemDependency;
// When true, this value indicates that the package is a dependency of the
// system and so cannot be uninstalled.
PackageState fState;
PackageInstallationLocationSet
fInstallationLocations;
float fDownloadProgress;
};
#endif // PACKAGE_LOCAL_INFO_H #endif // PACKAGE_LOCAL_INFO_H
@@ -1,5 +1,5 @@
/* /*
* Copyright 2024, Andrew Lindesay <[email protected]>. * Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
@@ -117,3 +117,127 @@ PackageLocalizedText::operator!=(const PackageLocalizedText& other) const
{ {
return !(*this == other); return !(*this == other);
} }
// #pragma mark - PackageLocalInfoBuilder
PackageLocalizedTextBuilder::PackageLocalizedTextBuilder()
:
fTitle(),
fSummary(),
fDescription(),
fHasChangelog(false),
fChangelog()
{
}
PackageLocalizedTextBuilder::PackageLocalizedTextBuilder(const PackageLocalizedTextRef& value)
:
fTitle(),
fSummary(),
fDescription(),
fHasChangelog(false),
fChangelog()
{
fSource = value;
}
PackageLocalizedTextBuilder::~PackageLocalizedTextBuilder()
{
}
void
PackageLocalizedTextBuilder::_InitFromSource()
{
if (fSource.IsSet()) {
_Init(fSource);
fSource.Unset();
}
}
void
PackageLocalizedTextBuilder::_Init(const PackageLocalizedText* value)
{
fTitle = value->Title();
fSummary = value->Summary();
fDescription = value->Description();
fHasChangelog = value->HasChangelog();
fChangelog = value->Changelog();
}
PackageLocalizedTextRef
PackageLocalizedTextBuilder::BuildRef()
{
if (fSource.IsSet())
return fSource;
PackageLocalizedText* localizedText = new PackageLocalizedText();
localizedText->SetTitle(fTitle);
localizedText->SetSummary(fSummary);
localizedText->SetDescription(fDescription);
localizedText->SetHasChangelog(fHasChangelog);
localizedText->SetChangelog(fChangelog);
return PackageLocalizedTextRef(localizedText, true);
}
PackageLocalizedTextBuilder&
PackageLocalizedTextBuilder::WithTitle(const BString& value)
{
if (!fSource.IsSet() || fSource->Title() != value) {
_InitFromSource();
fTitle = value;
}
return *this;
}
PackageLocalizedTextBuilder&
PackageLocalizedTextBuilder::WithSummary(const BString& value)
{
if (!fSource.IsSet() || fSource->Summary() != value) {
_InitFromSource();
fSummary = value;
}
return *this;
}
PackageLocalizedTextBuilder&
PackageLocalizedTextBuilder::WithDescription(const BString& value)
{
if (!fSource.IsSet() || fSource->Description() != value) {
_InitFromSource();
fDescription = value;
}
return *this;
}
PackageLocalizedTextBuilder&
PackageLocalizedTextBuilder::WithHasChangelog(bool value)
{
if (!fSource.IsSet() || fSource->HasChangelog() != value) {
_InitFromSource();
fHasChangelog = value;
}
return *this;
}
PackageLocalizedTextBuilder&
PackageLocalizedTextBuilder::WithChangelog(const BString& value)
{
if (!fSource.IsSet() || fSource->Changelog() != value) {
_InitFromSource();
fChangelog = value;
}
return *this;
}
@@ -1,5 +1,5 @@
/* /*
* Copyright 2024, Andrew Lindesay <[email protected]>. * Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef PACKAGE_LOCALIZED_TEXT_H #ifndef PACKAGE_LOCALIZED_TEXT_H
@@ -10,30 +10,37 @@
#include <String.h> #include <String.h>
class PackageLocalizedTextBuilder;
/*! Instances of this class should not be created directly; instead use the
PackageLocalizedTextBuilder class as a builder-constructor.
*/
class PackageLocalizedText : public BReferenceable class PackageLocalizedText : public BReferenceable
{ {
friend class PackageLocalizedTextBuilder;
public: public:
PackageLocalizedText(); PackageLocalizedText();
PackageLocalizedText(const PackageLocalizedText& other); PackageLocalizedText(const PackageLocalizedText& other);
const BString& Title() const;
void SetTitle(const BString& value);
const BString& Summary() const;
void SetSummary(const BString& value);
const BString& Description() const;
void SetDescription(const BString& value);
const bool HasChangelog() const;
void SetHasChangelog(bool value);
void SetChangelog(const BString& value);
const BString& Changelog() const;
bool operator==(const PackageLocalizedText& other) const; bool operator==(const PackageLocalizedText& other) const;
bool operator!=(const PackageLocalizedText& other) const; bool operator!=(const PackageLocalizedText& other) const;
const BString& Title() const;
const BString& Summary() const;
const BString& Description() const;
const bool HasChangelog() const;
const BString& Changelog() const;
private:
void SetTitle(const BString& value);
void SetSummary(const BString& value);
void SetDescription(const BString& value);
void SetHasChangelog(bool value);
void SetChangelog(const BString& value);
private: private:
BString fTitle; BString fTitle;
BString fSummary; BString fSummary;
@@ -47,4 +54,40 @@ private:
typedef BReference<PackageLocalizedText> PackageLocalizedTextRef; typedef BReference<PackageLocalizedText> PackageLocalizedTextRef;
class PackageLocalizedTextBuilder
{
public:
PackageLocalizedTextBuilder();
PackageLocalizedTextBuilder(const PackageLocalizedTextRef& value);
virtual ~PackageLocalizedTextBuilder();
PackageLocalizedTextRef
BuildRef();
PackageLocalizedTextBuilder&
WithTitle(const BString& value);
PackageLocalizedTextBuilder&
WithSummary(const BString& value);
PackageLocalizedTextBuilder&
WithDescription(const BString& value);
PackageLocalizedTextBuilder&
WithHasChangelog(bool value);
PackageLocalizedTextBuilder&
WithChangelog(const BString& value);
private:
void _InitFromSource();
void _Init(const PackageLocalizedText* value);
private:
PackageLocalizedTextRef
fSource;
BString fTitle;
BString fSummary;
BString fDescription;
bool fHasChangelog;
BString fChangelog;
};
#endif // PACKAGE_LOCALIZED_TEXT_H #endif // PACKAGE_LOCALIZED_TEXT_H
@@ -1,6 +1,6 @@
/* /*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>. * Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2016-2024, Andrew Lindesay <[email protected]>. * Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef PACKAGE_PUBLISHER_INFO_H #ifndef PACKAGE_PUBLISHER_INFO_H
@@ -33,6 +33,8 @@ private:
BString fWebsite; BString fWebsite;
}; };
typedef BReference<PackagePublisherInfo> PackagePublisherInfoRef; typedef BReference<PackagePublisherInfo> PackagePublisherInfoRef;
#endif // PACKAGE_PUBLISHER_INFO_H #endif // PACKAGE_PUBLISHER_INFO_H
@@ -1,5 +1,5 @@
/* /*
* Copyright 2024, Andrew Lindesay <[email protected]>. * Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
@@ -19,14 +19,6 @@ PackageScreenshotInfo::PackageScreenshotInfo(const PackageScreenshotInfo& other)
} }
PackageScreenshotInfo&
PackageScreenshotInfo::operator=(const PackageScreenshotInfo& other)
{
fScreenshotInfos = other.fScreenshotInfos;
return *this;
}
bool bool
PackageScreenshotInfo::operator==(const PackageScreenshotInfo& other) const PackageScreenshotInfo::operator==(const PackageScreenshotInfo& other) const
{ {
@@ -41,13 +33,6 @@ PackageScreenshotInfo::operator!=(const PackageScreenshotInfo& other) const
} }
void
PackageScreenshotInfo::Clear()
{
fScreenshotInfos.clear();
}
int32 int32
PackageScreenshotInfo::Count() const PackageScreenshotInfo::Count() const
{ {
@@ -55,7 +40,7 @@ PackageScreenshotInfo::Count() const
} }
ScreenshotInfoRef const ScreenshotInfoRef
PackageScreenshotInfo::ScreenshotAtIndex(int32 index) const PackageScreenshotInfo::ScreenshotAtIndex(int32 index) const
{ {
return fScreenshotInfos[index]; return fScreenshotInfos[index];
@@ -66,4 +51,73 @@ void
PackageScreenshotInfo::AddScreenshot(const ScreenshotInfoRef& info) PackageScreenshotInfo::AddScreenshot(const ScreenshotInfoRef& info)
{ {
fScreenshotInfos.push_back(info); fScreenshotInfos.push_back(info);
} }
// #pragma mark - PackageScreenshotInfoBuilder
PackageScreenshotInfoBuilder::PackageScreenshotInfoBuilder()
:
fSource(),
fScreenshotInfos()
{
}
PackageScreenshotInfoBuilder::PackageScreenshotInfoBuilder(const PackageScreenshotInfoRef& other)
:
fScreenshotInfos()
{
fSource = other;
}
PackageScreenshotInfoBuilder::~PackageScreenshotInfoBuilder()
{
}
void
PackageScreenshotInfoBuilder::_InitFromSource()
{
if (fSource.IsSet()) {
_Init(fSource.Get());
fSource.Unset();
}
}
void
PackageScreenshotInfoBuilder::_Init(const PackageScreenshotInfo* value)
{
int32 count = value->Count();
for (int32 i = 0; i < count; i++)
fScreenshotInfos.push_back(value->ScreenshotAtIndex(i));
}
PackageScreenshotInfoRef
PackageScreenshotInfoBuilder::BuildRef()
{
if (fSource.IsSet())
return fSource;
PackageScreenshotInfo* screenshotInfo = new PackageScreenshotInfo();
std::vector<ScreenshotInfoRef>::const_iterator it;
for (it = fScreenshotInfos.begin(); it != fScreenshotInfos.end(); it++)
screenshotInfo->AddScreenshot(*it);
return PackageScreenshotInfoRef(screenshotInfo, true);
}
PackageScreenshotInfoBuilder&
PackageScreenshotInfoBuilder::AddScreenshot(const ScreenshotInfoRef& value)
{
if (fSource.IsSet())
_InitFromSource();
fScreenshotInfos.push_back(value);
return *this;
}
@@ -1,5 +1,5 @@
/* /*
* Copyright 2024, Andrew Lindesay <[email protected]>. * Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef PACKAGE_SCREENSHOT_INFO_H #ifndef PACKAGE_SCREENSHOT_INFO_H
@@ -14,21 +14,28 @@
#include "ScreenshotInfo.h" #include "ScreenshotInfo.h"
class PackageScreenshotInfoBuilder;
/*! Instances of this class should not be created directly; instead use the
PackageScreenshotInfoBuilder class as a builder-constructor.
*/
class PackageScreenshotInfo : public BReferenceable class PackageScreenshotInfo : public BReferenceable
{ {
friend class PackageScreenshotInfoBuilder;
public: public:
PackageScreenshotInfo(); PackageScreenshotInfo();
PackageScreenshotInfo(const PackageScreenshotInfo& other); PackageScreenshotInfo(const PackageScreenshotInfo& other);
PackageScreenshotInfo&
operator=(const PackageScreenshotInfo& other);
bool operator==(const PackageScreenshotInfo& other) const; bool operator==(const PackageScreenshotInfo& other) const;
bool operator!=(const PackageScreenshotInfo& other) const; bool operator!=(const PackageScreenshotInfo& other) const;
void Clear();
void AddScreenshot(const ScreenshotInfoRef& info);
int32 Count() const; int32 Count() const;
ScreenshotInfoRef ScreenshotAtIndex(int32 index) const; const ScreenshotInfoRef ScreenshotAtIndex(int32 index) const;
private:
void AddScreenshot(const ScreenshotInfoRef& info);
private: private:
std::vector<ScreenshotInfoRef> std::vector<ScreenshotInfoRef>
@@ -39,4 +46,29 @@ private:
typedef BReference<PackageScreenshotInfo> PackageScreenshotInfoRef; typedef BReference<PackageScreenshotInfo> PackageScreenshotInfoRef;
class PackageScreenshotInfoBuilder
{
public:
PackageScreenshotInfoBuilder();
PackageScreenshotInfoBuilder(const PackageScreenshotInfoRef& other);
virtual ~PackageScreenshotInfoBuilder();
PackageScreenshotInfoRef
BuildRef();
PackageScreenshotInfoBuilder&
AddScreenshot(const ScreenshotInfoRef& value);
private:
void _InitFromSource();
void _Init(const PackageScreenshotInfo* value);
private:
PackageScreenshotInfoRef
fSource;
std::vector<ScreenshotInfoRef>
fScreenshotInfos;
};
#endif // PACKAGE_SCREENSHOT_INFO_H #endif // PACKAGE_SCREENSHOT_INFO_H
@@ -0,0 +1,206 @@
/*
* Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "PackageUserRatingInfo.h"
PackageUserRatingInfo::PackageUserRatingInfo()
:
fUserRatings(),
fSummary(),
fUserRatingsPopulated(false)
{
}
PackageUserRatingInfo::PackageUserRatingInfo(const PackageUserRatingInfo& other)
:
fSummary(other.Summary()),
fUserRatingsPopulated(other.UserRatingsPopulated())
{
for (int32 i = CountUserRatings() - 1; i >= 0; i--)
AddUserRating(other.UserRatingAtIndex(i));
}
const UserRatingSummaryRef
PackageUserRatingInfo::Summary() const
{
return fSummary;
}
void
PackageUserRatingInfo::SetSummary(UserRatingSummaryRef value)
{
fSummary = value;
}
void
PackageUserRatingInfo::SetUserRatingsPopulated()
{
fUserRatingsPopulated = true;
}
bool
PackageUserRatingInfo::UserRatingsPopulated() const
{
return fUserRatingsPopulated;
}
int32
PackageUserRatingInfo::CountUserRatings() const
{
return fUserRatings.size();
}
const UserRatingRef
PackageUserRatingInfo::UserRatingAtIndex(int32 index) const
{
return fUserRatings[index];
}
void
PackageUserRatingInfo::AddUserRating(const UserRatingRef& rating)
{
fUserRatings.push_back(rating);
}
bool
PackageUserRatingInfo::operator==(const PackageUserRatingInfo& other) const
{
if (Summary() != other.Summary())
return false;
if (UserRatingsPopulated() != other.UserRatingsPopulated())
return false;
if (CountUserRatings() != other.CountUserRatings())
return false;
for (int32 i = CountUserRatings() - 1; i >= 0; i--) {
if (other.UserRatingAtIndex(i) != UserRatingAtIndex(i))
return false;
}
return true;
}
bool
PackageUserRatingInfo::operator!=(const PackageUserRatingInfo& other) const
{
return !(*this == other);
}
// #pragma mark - PackageUserRatingInfoBuilder
PackageUserRatingInfoBuilder::PackageUserRatingInfoBuilder()
:
fSource(),
fUserRatings(),
fSummary(),
fUserRatingsPopulated(false)
{
}
PackageUserRatingInfoBuilder::PackageUserRatingInfoBuilder(const PackageUserRatingInfoRef& other)
:
fSource(other),
fUserRatings(),
fSummary(),
fUserRatingsPopulated(false)
{
}
PackageUserRatingInfoBuilder::~PackageUserRatingInfoBuilder()
{
}
PackageUserRatingInfoRef
PackageUserRatingInfoBuilder::BuildRef()
{
if (fSource.IsSet())
return fSource;
PackageUserRatingInfo* userRatingInfo = new PackageUserRatingInfo();
userRatingInfo->SetSummary(fSummary);
if (fUserRatingsPopulated)
userRatingInfo->SetUserRatingsPopulated();
std::vector<UserRatingRef>::const_iterator it;
for (it = fUserRatings.begin(); it != fUserRatings.end(); it++)
userRatingInfo->AddUserRating(*it);
return PackageUserRatingInfoRef(userRatingInfo, true);
}
PackageUserRatingInfoBuilder&
PackageUserRatingInfoBuilder::AddUserRating(const UserRatingRef& rating)
{
if (fSource.IsSet())
_InitFromSource();
fUserRatings.push_back(rating);
return *this;
}
PackageUserRatingInfoBuilder&
PackageUserRatingInfoBuilder::WithSummary(UserRatingSummaryRef value)
{
if (!fSource.IsSet() || fSource->Summary() != value) {
_InitFromSource();
fSummary = value;
}
return *this;
}
PackageUserRatingInfoBuilder&
PackageUserRatingInfoBuilder::WithUserRatingsPopulated()
{
if (!fSource.IsSet() || !fSource->UserRatingsPopulated()) {
_InitFromSource();
fUserRatingsPopulated = true;
}
return *this;
}
void
PackageUserRatingInfoBuilder::_InitFromSource()
{
if (fSource.IsSet()) {
_Init(fSource.Get());
fSource.Unset();
}
}
void
PackageUserRatingInfoBuilder::_Init(const PackageUserRatingInfo* value)
{
fSummary = value->Summary();
fUserRatingsPopulated = value->UserRatingsPopulated();
int32 count = value->CountUserRatings();
for (int32 i = 0; i < count; i++)
fUserRatings.push_back(value->UserRatingAtIndex(i));
}
@@ -0,0 +1,92 @@
/*
* Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PACKAGE_USER_RATING_INFO_H
#define PACKAGE_USER_RATING_INFO_H
#include <vector>
#include <Referenceable.h>
#include "UserRating.h"
#include "UserRatingSummary.h"
class PackageUserRatingInfoBuilder;
/*! This class carries the collection of user ratings related to a package but
also provides an instance of `UserRatingSummaryRef` which is a summary of
the user ratings.
Instances of this class should not be created directly; instead use the
PackageUserRatingInfoBuilder class as a builder-constructor.
*/
class PackageUserRatingInfo : public BReferenceable
{
friend class PackageUserRatingInfoBuilder;
public:
PackageUserRatingInfo();
PackageUserRatingInfo(const PackageUserRatingInfo& other);
bool operator==(const PackageUserRatingInfo& other) const;
bool operator!=(const PackageUserRatingInfo& other) const;
const UserRatingSummaryRef
Summary() const;
int32 CountUserRatings() const;
const UserRatingRef UserRatingAtIndex(int32 index) const;
bool UserRatingsPopulated() const;
private:
void SetSummary(UserRatingSummaryRef value);
void AddUserRating(const UserRatingRef& rating);
void SetUserRatingsPopulated();
private:
std::vector<UserRatingRef>
fUserRatings;
UserRatingSummaryRef
fSummary;
bool fUserRatingsPopulated;
};
typedef BReference<PackageUserRatingInfo> PackageUserRatingInfoRef;
class PackageUserRatingInfoBuilder
{
public:
PackageUserRatingInfoBuilder();
PackageUserRatingInfoBuilder(const PackageUserRatingInfoRef& other);
virtual ~PackageUserRatingInfoBuilder();
PackageUserRatingInfoRef
BuildRef();
PackageUserRatingInfoBuilder&
AddUserRating(const UserRatingRef& rating);
PackageUserRatingInfoBuilder&
WithSummary(UserRatingSummaryRef value);
PackageUserRatingInfoBuilder&
WithUserRatingsPopulated();
private:
void _InitFromSource();
void _Init(const PackageUserRatingInfo* value);
private:
PackageUserRatingInfoRef
fSource;
std::vector<UserRatingRef>
fUserRatings;
UserRatingSummaryRef
fSummary;
bool fUserRatingsPopulated;
};
#endif // PACKAGE_USER_RATING_INFO_H
@@ -1,5 +1,5 @@
/* /*
* Copyright 2024, Andrew Lindesay <[email protected]>. * Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
@@ -15,6 +15,14 @@ PackageVersion::PackageVersion()
} }
PackageVersion::PackageVersion(uint64 createTimestamp)
:
BPackageVersion(),
fCreateTimestamp(createTimestamp)
{
}
PackageVersion::PackageVersion(const PackageVersion& other) PackageVersion::PackageVersion(const PackageVersion& other)
: :
BPackageVersion(other), BPackageVersion(other),
@@ -23,6 +31,14 @@ PackageVersion::PackageVersion(const PackageVersion& other)
} }
PackageVersion::PackageVersion(const PackageVersion& other, uint64 createTimestamp)
:
BPackageVersion(other),
fCreateTimestamp(createTimestamp)
{
}
PackageVersion::PackageVersion(const BPackageVersion& other) PackageVersion::PackageVersion(const BPackageVersion& other)
: :
BPackageVersion(other), BPackageVersion(other),
@@ -36,22 +52,10 @@ PackageVersion::~PackageVersion()
} }
void uint64
PackageVersion::SetCreateTimestamp(uint64 value) PackageVersion::CreateTimestamp() const
{ {
fCreateTimestamp = value; return fCreateTimestamp;
}
PackageVersion&
PackageVersion::operator=(const PackageVersion& other)
{
if (this != &other) {
// can't use BPackageVersion operator= possibly because it is inlined?
SetTo(other.Major(), other.Minor(), other.Micro(), other.PreRelease(), other.Revision());
fCreateTimestamp = other.fCreateTimestamp;
}
return *this;
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright 2024, Andrew Lindesay <[email protected]>. * Copyright 2024-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef PACKAGE_VERSION_H #ifndef PACKAGE_VERSION_H
@@ -18,17 +18,16 @@ class PackageVersion : public BPackageVersion, public BReferenceable
{ {
public: public:
PackageVersion(); PackageVersion();
PackageVersion(uint64 createTimestamp);
PackageVersion(const PackageVersion& other); PackageVersion(const PackageVersion& other);
PackageVersion(const PackageVersion& other, uint64 createTimestamp);
PackageVersion(const BPackageVersion& other); PackageVersion(const BPackageVersion& other);
virtual ~PackageVersion(); virtual ~PackageVersion();
PackageVersion& operator=(const PackageVersion& other);
bool operator==(const PackageVersion& other) const; bool operator==(const PackageVersion& other) const;
bool operator!=(const PackageVersion& other) const; bool operator!=(const PackageVersion& other) const;
uint64 CreateTimestamp() const uint64 CreateTimestamp() const;
{ return fCreateTimestamp; }
void SetCreateTimestamp(uint64 value);
private: private:
uint64 fCreateTimestamp; uint64 fCreateTimestamp;
@@ -1,7 +1,7 @@
/* /*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>. * Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2013, Rene Gollent <[email protected]>. * Copyright 2013, Rene Gollent <[email protected]>.
* Copyright 2016-2023, Andrew Lindesay <[email protected]>. * Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
@@ -30,14 +30,6 @@ UserInfo::UserInfo(const UserInfo& other)
} }
UserInfo&
UserInfo::operator=(const UserInfo& other)
{
fNickName = other.fNickName;
return *this;
}
bool bool
UserInfo::operator==(const UserInfo& other) const UserInfo::operator==(const UserInfo& other) const
{ {
+1 -2
View File
@@ -1,6 +1,6 @@
/* /*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>. * Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2016-2023, Andrew Lindesay <[email protected]>. * Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef USER_INFO_H #ifndef USER_INFO_H
@@ -16,7 +16,6 @@ public:
UserInfo(const BString& nickName); UserInfo(const BString& nickName);
UserInfo(const UserInfo& other); UserInfo(const UserInfo& other);
UserInfo& operator=(const UserInfo& other);
bool operator==(const UserInfo& other) const; bool operator==(const UserInfo& other) const;
bool operator!=(const UserInfo& other) const; bool operator!=(const UserInfo& other) const;
@@ -1,7 +1,7 @@
/* /*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>. * Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2013, Rene Gollent <[email protected]>. * Copyright 2013, Rene Gollent <[email protected]>.
* Copyright 2016-2024, Andrew Lindesay <[email protected]>. * Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
@@ -47,19 +47,6 @@ UserRating::UserRating(const UserRating& other)
} }
UserRating&
UserRating::operator=(const UserRating& other)
{
fUserInfo = other.fUserInfo;
fRating = other.fRating;
fComment = other.fComment;
fLanguageId = other.fLanguageId;
fPackageVersion = other.fPackageVersion;
fCreateTimestamp = other.fCreateTimestamp;
return *this;
}
bool bool
UserRating::operator==(const UserRating& other) const UserRating::operator==(const UserRating& other) const
{ {
@@ -1,6 +1,6 @@
/* /*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>. * Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2016-2024, Andrew Lindesay <[email protected]>. * Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef USER_RATING_H #ifndef USER_RATING_H
@@ -23,7 +23,6 @@ public:
uint64 createTimestamp); uint64 createTimestamp);
UserRating(const UserRating& other); UserRating(const UserRating& other);
UserRating& operator=(const UserRating& other);
bool operator==(const UserRating& other) const; bool operator==(const UserRating& other) const;
bool operator!=(const UserRating& other) const; bool operator!=(const UserRating& other) const;
@@ -1,128 +0,0 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "UserRatingInfo.h"
UserRatingInfo::UserRatingInfo()
:
fUserRatings(),
fSummary(),
fUserRatingsPopulated(false)
{
}
UserRatingInfo::UserRatingInfo(const UserRatingInfo& other)
:
fSummary(other.Summary()),
fUserRatingsPopulated(other.UserRatingsPopulated())
{
for (int32 i = CountUserRatings() - 1; i >= 0; i--)
AddUserRating(other.UserRatingAtIndex(i));
}
void
UserRatingInfo::Clear()
{
fSummary.Unset();
ClearUserRatings();
}
void
UserRatingInfo::SetSummary(UserRatingSummaryRef value)
{
fSummary = value;
}
void
UserRatingInfo::SetUserRatingsPopulated()
{
fUserRatingsPopulated = true;
}
bool
UserRatingInfo::UserRatingsPopulated() const
{
return fUserRatingsPopulated;
}
void
UserRatingInfo::ClearUserRatings()
{
fUserRatings.clear();
fUserRatingsPopulated = false;
}
int32
UserRatingInfo::CountUserRatings() const
{
return fUserRatings.size();
}
UserRatingRef
UserRatingInfo::UserRatingAtIndex(int32 index) const
{
return fUserRatings[index];
}
void
UserRatingInfo::AddUserRating(const UserRatingRef& rating)
{
fUserRatings.push_back(rating);
}
UserRatingInfo&
UserRatingInfo::operator=(const UserRatingInfo& other)
{
fSummary = other.Summary();
fUserRatings.clear();
fUserRatingsPopulated = other.UserRatingsPopulated();
for (int32 i = CountUserRatings() - 1; i >= 0; i--)
AddUserRating(other.UserRatingAtIndex(i));
return *this;
}
bool
UserRatingInfo::operator==(const UserRatingInfo& other) const
{
if (Summary() != other.Summary())
return false;
if (UserRatingsPopulated() != other.UserRatingsPopulated())
return false;
if (CountUserRatings() != other.CountUserRatings())
return false;
for (int32 i = CountUserRatings() - 1; i >= 0; i--) {
if (other.UserRatingAtIndex(i) != UserRatingAtIndex(i))
return false;
}
return true;
}
bool
UserRatingInfo::operator!=(const UserRatingInfo& other) const
{
return !(*this == other);
}
@@ -1,56 +0,0 @@
/*
* Copyright 2024, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef USER_RATING_INFO_H
#define USER_RATING_INFO_H
#include <vector>
#include <Referenceable.h>
#include "UserRating.h"
#include "UserRatingSummary.h"
/*! This class carries the collection of user ratings related to a package but also provides
an instance of `UserRatingSummaryRef` which is a summary of the user ratings.
*/
class UserRatingInfo : public BReferenceable
{
public:
UserRatingInfo();
UserRatingInfo(const UserRatingInfo& other);
void Clear();
const UserRatingSummaryRef
Summary() const
{ return fSummary; }
void SetSummary(UserRatingSummaryRef value);
void ClearUserRatings();
void AddUserRating(const UserRatingRef& rating);
int32 CountUserRatings() const;
UserRatingRef UserRatingAtIndex(int32 index) const;
void SetUserRatingsPopulated();
bool UserRatingsPopulated() const;
UserRatingInfo& operator=(const UserRatingInfo& other);
bool operator==(const UserRatingInfo& other) const;
bool operator!=(const UserRatingInfo& other) const;
private:
std::vector<UserRatingRef>
fUserRatings;
UserRatingSummaryRef
fSummary;
bool fUserRatingsPopulated;
};
typedef BReference<UserRatingInfo> UserRatingInfoRef;
#endif // USER_RATING_SUMMARY_H
@@ -1,7 +1,7 @@
/* /*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>. * Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2013, Rene Gollent <[email protected]>. * Copyright 2013, Rene Gollent <[email protected]>.
* Copyright 2016-2024, Andrew Lindesay <[email protected]>. * Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
@@ -18,7 +18,7 @@ UserRatingSummary::UserRatingSummary()
fRatingCount(0), fRatingCount(0),
fRatingCountNoStar(0) fRatingCountNoStar(0)
{ {
for (int i = 0; i < 6; i++) for (int i = 0; i <= 5; i++)
fRatingCountByStar[i] = 0; fRatingCountByStar[i] = 0;
} }
@@ -34,15 +34,17 @@ UserRatingSummary::UserRatingSummary(const UserRatingSummary& other)
} }
void float
UserRatingSummary::Clear() UserRatingSummary::AverageRating() const
{ {
fAverageRating = RATING_MISSING; return fAverageRating;
fRatingCount = 0; }
fRatingCountNoStar = 0;
for (int i = 0; i < 6; i++)
fRatingCountByStar[i] = 0; int
UserRatingSummary::RatingCount() const
{
return fRatingCount;
} }
@@ -93,20 +95,6 @@ UserRatingSummary::SetRatingByStar(int star, int ratingCount)
} }
UserRatingSummary&
UserRatingSummary::operator=(const UserRatingSummary& other)
{
fAverageRating = other.AverageRating();
fRatingCount = other.RatingCount();
fRatingCountNoStar = other.RatingCountByStar(-1);
for (int i = 0; i < 6; i++)
fRatingCountByStar[i] = other.RatingCountByStar(i);
return *this;
}
bool bool
UserRatingSummary::operator==(const UserRatingSummary& other) const UserRatingSummary::operator==(const UserRatingSummary& other) const
{ {
@@ -129,3 +117,117 @@ UserRatingSummary::operator!=(const UserRatingSummary& other) const
{ {
return !(*this == other); return !(*this == other);
} }
// #pragma mark - UserRatingSummaryBuilder
UserRatingSummaryBuilder::UserRatingSummaryBuilder()
:
fAverageRating(RATING_MISSING),
fRatingCount(0),
fRatingCountNoStar(0)
{
for (int i = 0; i < 6; i++)
fRatingCountByStar[i] = 0;
}
UserRatingSummaryBuilder::UserRatingSummaryBuilder(const UserRatingSummaryRef& other)
:
fSource(),
fAverageRating(RATING_MISSING),
fRatingCount(0),
fRatingCountNoStar(0)
{
for (int i = 0; i < 6; i++)
fRatingCountByStar[i] = other->RatingCountByStar(i);
}
UserRatingSummaryBuilder::~UserRatingSummaryBuilder()
{
}
UserRatingSummaryRef
UserRatingSummaryBuilder::BuildRef()
{
if (fSource.IsSet())
return fSource;
UserRatingSummary* userRatingSummary = new UserRatingSummary();
userRatingSummary->SetAverageRating(fAverageRating);
userRatingSummary->SetRatingCount(fRatingCount);
userRatingSummary->SetRatingByStar(RATING_MISSING_STAR, fRatingCountNoStar);
for (int star = 0; star <= 5; star++)
userRatingSummary->SetRatingByStar(star, fRatingCountByStar[star]);
return UserRatingSummaryRef(userRatingSummary, true);
}
UserRatingSummaryBuilder&
UserRatingSummaryBuilder::WithAverageRating(float value)
{
if (!fSource.IsSet() || fSource->AverageRating() != value) {
_InitFromSource();
fAverageRating = value;
}
return *this;
}
UserRatingSummaryBuilder&
UserRatingSummaryBuilder::WithRatingCount(int value)
{
if (!fSource.IsSet() || fSource->RatingCount() != value) {
_InitFromSource();
fRatingCount = value;
}
return *this;
}
UserRatingSummaryBuilder&
UserRatingSummaryBuilder::AddRatingByStar(int star, int ratingCount)
{
if (star < RATING_MISSING_STAR || star > 5) {
HDERROR("bad star %" B_PRId32 " in builder", star);
return *this;
}
if (!fSource.IsSet() || fSource->RatingCountByStar(star) != ratingCount) {
_InitFromSource();
if (star == RATING_MISSING_STAR)
fRatingCountNoStar = ratingCount;
else
fRatingCountByStar[star] = ratingCount;
}
return *this;
}
void
UserRatingSummaryBuilder::_InitFromSource()
{
if (fSource.IsSet()) {
_Init(fSource.Get());
fSource.Unset();
}
}
void
UserRatingSummaryBuilder::_Init(const UserRatingSummary* value)
{
fAverageRating = value->AverageRating();
fRatingCount = value->RatingCount();
fRatingCountNoStar = value->RatingCountByStar(RATING_MISSING_STAR);
for (int i = 0; i <= 5; i++)
fRatingCountByStar[i] = value->RatingCountByStar(i);
}
@@ -1,6 +1,6 @@
/* /*
* Copyright 2013-2014, Stephan Aßmus <[email protected]>. * Copyright 2013-2014, Stephan Aßmus <[email protected]>.
* Copyright 2016-2024, Andrew Lindesay <[email protected]>. * Copyright 2016-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef USER_RATING_SUMMARY_H #ifndef USER_RATING_SUMMARY_H
@@ -15,39 +15,70 @@
include users' ratings for every single version of a package but only the last include users' ratings for every single version of a package but only the last
one. Other rules also apply. For this reason, the calculation is performed on one. Other rules also apply. For this reason, the calculation is performed on
the server-side and presented here. the server-side and presented here.
*/
Instances of this class should not be created directly; instead use the
UserRatingSummaryBuilder class as a builder-constructor.
*/
class UserRatingSummary : public BReferenceable class UserRatingSummary : public BReferenceable
{ {
friend class UserRatingSummaryBuilder;
public: public:
UserRatingSummary(); UserRatingSummary();
UserRatingSummary(const UserRatingSummary& other); UserRatingSummary(const UserRatingSummary& other);
void Clear(); bool operator==(const UserRatingSummary& other) const;
bool operator!=(const UserRatingSummary& other) const;
float AverageRating() const float AverageRating() const;
{ return fAverageRating; } int RatingCount() const;
int RatingCount() const
{ return fRatingCount; }
int RatingCountByStar(int star) const; int RatingCountByStar(int star) const;
private:
void SetAverageRating(float value); void SetAverageRating(float value);
void SetRatingCount(int value); void SetRatingCount(int value);
void SetRatingByStar(int star, int ratingCount); void SetRatingByStar(int star, int ratingCount);
UserRatingSummary& operator=(const UserRatingSummary& other);
bool operator==(const UserRatingSummary& other) const;
bool operator!=(const UserRatingSummary& other) const;
private: private:
float fAverageRating; float fAverageRating;
int fRatingCount; int fRatingCount;
int fRatingCountNoStar; int fRatingCountNoStar;
int fRatingCountByStar[6]; int fRatingCountByStar[6];
}; };
typedef BReference<UserRatingSummary> UserRatingSummaryRef; typedef BReference<UserRatingSummary> UserRatingSummaryRef;
class UserRatingSummaryBuilder
{
public:
UserRatingSummaryBuilder();
UserRatingSummaryBuilder(const UserRatingSummaryRef& other);
virtual ~UserRatingSummaryBuilder();
UserRatingSummaryRef
BuildRef();
UserRatingSummaryBuilder&
WithAverageRating(float value);
UserRatingSummaryBuilder&
WithRatingCount(int value);
UserRatingSummaryBuilder&
AddRatingByStar(int star, int ratingCount);
private:
void _InitFromSource();
void _Init(const UserRatingSummary* value);
private:
UserRatingSummaryRef
fSource;
float fAverageRating;
int fRatingCount;
int fRatingCountNoStar;
int fRatingCountByStar[6];
};
#endif // USER_RATING_SUMMARY_H #endif // USER_RATING_SUMMARY_H
+30 -10
View File
@@ -1,12 +1,12 @@
/* /*
* Copyright 2018-2022, Andrew Lindesay <[email protected]>. * Copyright 2018-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#include "AbstractProcess.h" #include "AbstractProcess.h"
#include <unistd.h>
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include <AutoDeleter.h> #include <AutoDeleter.h>
#include <AutoLocker.h> #include <AutoLocker.h>
@@ -18,6 +18,9 @@
#include "ProcessListener.h" #include "ProcessListener.h"
static const bigtime_t kProcessUpdateMinimumDelay = 250000;
AbstractProcess::AbstractProcess() AbstractProcess::AbstractProcess()
: :
fLock(), fLock(),
@@ -25,7 +28,8 @@ AbstractProcess::AbstractProcess()
fWasStopped(false), fWasStopped(false),
fProcessState(PROCESS_INITIAL), fProcessState(PROCESS_INITIAL),
fErrorStatus(B_OK), fErrorStatus(B_OK),
fDurationSeconds(0.0) fDurationSeconds(0.0),
fLastProgressUpdate(0)
{ {
} }
@@ -72,7 +76,7 @@ AbstractProcess::Run()
BStopWatch stopWatch("process", true); BStopWatch stopWatch("process", true);
status_t runResult = RunInternal(); status_t runResult = RunInternal();
fDurationSeconds = ((double) stopWatch.ElapsedTime() / 1000000.0); fDurationSeconds = static_cast<double>(stopWatch.ElapsedTime()) / 1000000.0;
if (runResult != B_OK) if (runResult != B_OK)
HDERROR("[%s] an error has arisen; %s", Name(), strerror(runResult)); HDERROR("[%s] an error has arisen; %s", Name(), strerror(runResult));
@@ -109,11 +113,11 @@ AbstractProcess::ErrorStatus()
} }
/*! This method will stop the process. The actual process may carry on to /*! This method will stop the process. The actual process may carry on to
perform some tidy-ups on its thread so this does not stop the thread or perform some tidy-ups on its thread so this does not stop the thread or
change the state of the process; just indicates to the running thread that change the state of the process; just indicates to the running thread that
it should stop. If it has not yet been started then it will be put into it should stop. If it has not yet been started then it will be put into
finished state. finished state.
*/ */
status_t status_t
@@ -196,6 +200,22 @@ AbstractProcess::LogReport()
} }
/*! This method should be called before processing any progress. It will prevent
progress updates from coming through too frequently.
*/
bool
AbstractProcess::_ShouldProcessProgress()
{
bigtime_t now = system_time();
if (now - fLastProgressUpdate < kProcessUpdateMinimumDelay)
return false;
fLastProgressUpdate = now;
return true;
}
/*static*/ char /*static*/ char
AbstractProcess::_ProcessStateIdentifier(process_state value) AbstractProcess::_ProcessStateIdentifier(process_state value)
{ {
@@ -209,4 +229,4 @@ AbstractProcess::_ProcessStateIdentifier(process_state value)
default: default:
return '?'; return '?';
} }
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2022, Andrew Lindesay <[email protected]>. * Copyright 2018-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef ABSTRACT_PROCESS_H #ifndef ABSTRACT_PROCESS_H
@@ -50,6 +50,8 @@ protected:
static char _ProcessStateIdentifier(process_state value); static char _ProcessStateIdentifier(process_state value);
bool _ShouldProcessProgress();
protected: protected:
BLocker fLock; BLocker fLock;
@@ -59,6 +61,7 @@ private:
process_state fProcessState; process_state fProcessState;
status_t fErrorStatus; status_t fErrorStatus;
double fDurationSeconds; double fDurationSeconds;
bigtime_t fLastProgressUpdate;
}; };
#endif // ABSTRACT_PROCESS_H #endif // ABSTRACT_PROCESS_H
@@ -1,11 +1,11 @@
/* /*
* Copyright 2018-2024, Andrew Lindesay <[email protected]>. * Copyright 2018-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#include "ProcessCoordinatorFactory.h" #include "ProcessCoordinatorFactory.h"
#include <Autolock.h>
#include <AutoLocker.h> #include <AutoLocker.h>
#include <Autolock.h>
#include <package/Context.h> #include <package/Context.h>
#include <package/PackageRoster.h> #include <package/PackageRoster.h>
@@ -42,13 +42,11 @@ using namespace BPackageKit;
/*static*/ ProcessCoordinator* /*static*/ ProcessCoordinator*
ProcessCoordinatorFactory::CreateIncrementViewCounter( ProcessCoordinatorFactory::CreateIncrementViewCounter(Model* model, const PackageInfoRef package)
Model* model, const PackageInfoRef package)
{ {
ProcessCoordinator* processCoordinator = new ProcessCoordinator( ProcessCoordinator* processCoordinator = new ProcessCoordinator("IncrementViewCounter");
"IncrementViewCounter"); AbstractProcessNode* node
AbstractProcessNode* node = new ThreadedProcessNode( = new ThreadedProcessNode(new IncrementViewCounterProcess(model, package));
new IncrementViewCounterProcess(model, package));
processCoordinator->AddNode(node); processCoordinator->AddNode(node);
return processCoordinator; return processCoordinator;
} }
@@ -56,56 +54,46 @@ ProcessCoordinatorFactory::CreateIncrementViewCounter(
/*static*/ ProcessCoordinator* /*static*/ ProcessCoordinator*
ProcessCoordinatorFactory::CreateUserDetailVerifierCoordinator( ProcessCoordinatorFactory::CreateUserDetailVerifierCoordinator(
UserDetailVerifierListener* userDetailVerifierListener, UserDetailVerifierListener* userDetailVerifierListener, Model* model)
Model* model)
{ {
ProcessCoordinator* processCoordinator = new ProcessCoordinator( ProcessCoordinator* processCoordinator = new ProcessCoordinator("UserDetailVerifier");
"UserDetailVerifier"); AbstractProcessNode* userDetailVerifier
AbstractProcessNode* userDetailVerifier = new ThreadedProcessNode( = new ThreadedProcessNode(new UserDetailVerifierProcess(model, userDetailVerifierListener));
new UserDetailVerifierProcess(model, userDetailVerifierListener));
processCoordinator->AddNode(userDetailVerifier); processCoordinator->AddNode(userDetailVerifier);
return processCoordinator; return processCoordinator;
} }
/* static */ ProcessCoordinator* /* static */ ProcessCoordinator*
ProcessCoordinatorFactory::CreateBulkLoadCoordinator( ProcessCoordinatorFactory::CreateBulkLoadCoordinator(Model* model, bool forceLocalUpdate)
PackageInfoListenerRef packageInfoListener,
Model* model, bool forceLocalUpdate)
{ {
bool areWorkingFilesAvailable = StorageUtils::AreWorkingFilesAvailable(); bool areWorkingFilesAvailable = StorageUtils::AreWorkingFilesAvailable();
uint32 serverProcessOptions = _CalculateServerProcessOptions(); uint32 serverProcessOptions = _CalculateServerProcessOptions();
BAutolock locker(model->Lock()); ProcessCoordinator* processCoordinator
ProcessCoordinator* processCoordinator = new ProcessCoordinator( = new ProcessCoordinator("BulkLoad", new BMessage(MSG_BULK_LOAD_DONE));
"BulkLoad", new BMessage(MSG_BULK_LOAD_DONE));
AbstractProcessNode *localRepositoryUpdate = AbstractProcessNode* localRepositoryUpdate
new ThreadedProcessNode(new LocalRepositoryUpdateProcess(model, = new ThreadedProcessNode(new LocalRepositoryUpdateProcess(model, forceLocalUpdate));
forceLocalUpdate));
processCoordinator->AddNode(localRepositoryUpdate); processCoordinator->AddNode(localRepositoryUpdate);
AbstractProcessNode *localPkgDataLoad = AbstractProcessNode* localPkgDataLoad
new ThreadedProcessNode(new LocalPkgDataLoadProcess( = new ThreadedProcessNode(new LocalPkgDataLoadProcess(model, forceLocalUpdate));
packageInfoListener, model, forceLocalUpdate));
localPkgDataLoad->AddPredecessor(localRepositoryUpdate); localPkgDataLoad->AddPredecessor(localRepositoryUpdate);
processCoordinator->AddNode(localPkgDataLoad); processCoordinator->AddNode(localPkgDataLoad);
if (areWorkingFilesAvailable) { if (areWorkingFilesAvailable) {
AbstractProcessNode *serverIconExportUpdate = AbstractProcessNode* serverIconExportUpdate = new ThreadedProcessNode(
new ThreadedProcessNode(new ServerIconExportUpdateProcess(model, new ServerIconExportUpdateProcess(model, serverProcessOptions));
serverProcessOptions));
serverIconExportUpdate->AddPredecessor(localPkgDataLoad); serverIconExportUpdate->AddPredecessor(localPkgDataLoad);
processCoordinator->AddNode(serverIconExportUpdate); processCoordinator->AddNode(serverIconExportUpdate);
AbstractProcessNode *serverRepositoryDataUpdate = AbstractProcessNode* serverRepositoryDataUpdate = new ThreadedProcessNode(
new ThreadedProcessNode(new ServerRepositoryDataUpdateProcess(model, new ServerRepositoryDataUpdateProcess(model, serverProcessOptions));
serverProcessOptions));
serverRepositoryDataUpdate->AddPredecessor(localPkgDataLoad); serverRepositoryDataUpdate->AddPredecessor(localPkgDataLoad);
processCoordinator->AddNode(serverRepositoryDataUpdate); processCoordinator->AddNode(serverRepositoryDataUpdate);
AbstractProcessNode *serverReferenceDataUpdate = AbstractProcessNode* serverReferenceDataUpdate = new ThreadedProcessNode(
new ThreadedProcessNode(new ServerReferenceDataUpdateProcess(model, new ServerReferenceDataUpdateProcess(model, serverProcessOptions));
serverProcessOptions));
processCoordinator->AddNode(serverReferenceDataUpdate); processCoordinator->AddNode(serverReferenceDataUpdate);
// This one has to run after the server data is taken up because it // This one has to run after the server data is taken up because it
@@ -113,8 +101,8 @@ ProcessCoordinatorFactory::CreateBulkLoadCoordinator(
// sourced from the server. It has all of the // sourced from the server. It has all of the
// `ServerPkgDataUpdateProcess` nodes configured as its predecessors. // `ServerPkgDataUpdateProcess` nodes configured as its predecessors.
AbstractProcessNode* populatePkgSizes = AbstractProcessNode* populatePkgSizes
new ThreadedProcessNode(new PopulatePkgSizesProcess(model)); = new ThreadedProcessNode(new PopulatePkgSizesProcess(model));
// create a process for each of the repositories that are configured on // create a process for each of the repositories that are configured on
// the local system. Later, only those that have a web-app repository // the local system. Later, only those that have a web-app repository
@@ -127,20 +115,19 @@ ProcessCoordinatorFactory::CreateBulkLoadCoordinator(
status_t repoNamesResult = roster.GetRepositoryNames(repoNames); status_t repoNamesResult = roster.GetRepositoryNames(repoNames);
if (repoNamesResult == B_OK) { if (repoNamesResult == B_OK) {
AutoLocker<BLocker> locker(model->Lock());
for (int32 i = 0; i < repoNames.CountStrings(); i++) { for (int32 i = 0; i < repoNames.CountStrings(); i++) {
AbstractProcessNode* processNode = new ThreadedProcessNode( AbstractProcessNode* processNode
new ServerPkgDataUpdateProcess( = new ThreadedProcessNode(new ServerPkgDataUpdateProcess(repoNames.StringAt(i),
repoNames.StringAt(i), model, serverProcessOptions)); model, serverProcessOptions));
processNode->AddPredecessor(serverRepositoryDataUpdate); processNode->AddPredecessor(serverRepositoryDataUpdate);
processNode->AddPredecessor(serverReferenceDataUpdate); processNode->AddPredecessor(serverReferenceDataUpdate);
processCoordinator->AddNode(processNode); processCoordinator->AddNode(processNode);
populatePkgSizes->AddPredecessor(processNode); populatePkgSizes->AddPredecessor(processNode);
} }
} else } else {
HDERROR("a problem has arisen getting the repository names."); HDERROR("a problem has arisen getting the repository names.");
}
processCoordinator->AddNode(populatePkgSizes); processCoordinator->AddNode(populatePkgSizes);
} }
@@ -150,8 +137,7 @@ ProcessCoordinatorFactory::CreateBulkLoadCoordinator(
/*static*/ ProcessCoordinator* /*static*/ ProcessCoordinator*
ProcessCoordinatorFactory::CreatePackageActionCoordinator( ProcessCoordinatorFactory::CreatePackageActionCoordinator(Model* model, BMessage* message)
Model* model, BMessage* message)
{ {
switch (message->what) { switch (message->what) {
case MSG_PKG_INSTALL: case MSG_PKG_INSTALL:
@@ -176,89 +162,80 @@ ProcessCoordinatorFactory::CacheScreenshotCoordinator(Model* model,
/*static*/ ProcessCoordinator* /*static*/ ProcessCoordinator*
ProcessCoordinatorFactory::PopulatePkgChangelogCoordinator(Model* model, PackageInfoRef package) ProcessCoordinatorFactory::PopulatePkgChangelogCoordinator(Model* model, const BString& packageName)
{ {
return _CreateSingleProcessCoordinator("PopulatePkgChangelog", return _CreateSingleProcessCoordinator("PopulatePkgChangelog",
new PopulatePkgChangelogFromServerProcess(package, model)); new PopulatePkgChangelogFromServerProcess(packageName, model));
} }
/*static*/ ProcessCoordinator* /*static*/ ProcessCoordinator*
ProcessCoordinatorFactory::PopulatePkgUserRatingsCoordinator(Model* model, PackageInfoRef package) ProcessCoordinatorFactory::PopulatePkgUserRatingsCoordinator(Model* model,
const BString& packageName)
{ {
return _CreateSingleProcessCoordinator("PopulatePkgUserRatings", return _CreateSingleProcessCoordinator("PopulatePkgUserRatings",
new PopulatePkgUserRatingsFromServerProcess(package, model)); new PopulatePkgUserRatingsFromServerProcess(packageName, model));
} }
/*static*/ PackageInfoRef /*static*/ BString
ProcessCoordinatorFactory::_ExtractPackageFromMessage( ProcessCoordinatorFactory::_ExtractPackageNameFromMessage(BMessage* message)
Model* model, BMessage* message)
{ {
BString pkgName; BString pkgName;
if (message->FindString(KEY_PACKAGE_NAME, &pkgName) != B_OK) if (message->FindString(KEY_PACKAGE_NAME, &pkgName) != B_OK)
HDFATAL("malformed message missing key [%s]", KEY_PACKAGE_NAME); HDFATAL("malformed message missing key [%s]", KEY_PACKAGE_NAME);
return model->PackageForName(pkgName); return pkgName;
} }
/*static*/ ProcessCoordinator* /*static*/ ProcessCoordinator*
ProcessCoordinatorFactory::_CreateInstallPackageActionCoordinator( ProcessCoordinatorFactory::_CreateInstallPackageActionCoordinator(Model* model, BMessage* message)
Model* model, BMessage* message)
{ {
ProcessCoordinator* processCoordinator = new ProcessCoordinator( ProcessCoordinator* processCoordinator
"InstallPackage", new BMessage(MSG_PACKAGE_ACTION_DONE)); = new ProcessCoordinator("InstallPackage", new BMessage(MSG_PACKAGE_ACTION_DONE));
PackageInfoRef package = _ExtractPackageFromMessage(model, message);
if (package.IsSet()) { AbstractProcessNode* processNode = new ThreadedProcessNode(
AbstractProcessNode *processNode = new InstallPackageProcess(_ExtractPackageNameFromMessage(message), model), 10);
new ThreadedProcessNode(
new InstallPackageProcess(package, model), 10); processCoordinator->AddNode(processNode);
processCoordinator->AddNode(processNode);
} else {
HDERROR("unable to find the package");
}
return processCoordinator; return processCoordinator;
} }
/*static*/ ProcessCoordinator* /*static*/ ProcessCoordinator*
ProcessCoordinatorFactory::_CreateUninstallPackageActionCoordinator( ProcessCoordinatorFactory::_CreateUninstallPackageActionCoordinator(Model* model, BMessage* message)
Model* model, BMessage* message)
{ {
ProcessCoordinator* processCoordinator = new ProcessCoordinator( ProcessCoordinator* processCoordinator
"UninstallPackage", new BMessage(MSG_PACKAGE_ACTION_DONE)); = new ProcessCoordinator("UninstallPackage", new BMessage(MSG_PACKAGE_ACTION_DONE));
PackageInfoRef package = _ExtractPackageFromMessage(model, message);
if (package.IsSet()) { AbstractProcessNode* processNode = new ThreadedProcessNode(
AbstractProcessNode *processNode = new UninstallPackageProcess(_ExtractPackageNameFromMessage(message), model), 10);
new ThreadedProcessNode(
new UninstallPackageProcess(package, model), 10); processCoordinator->AddNode(processNode);
processCoordinator->AddNode(processNode);
} else {
HDERROR("unable to find the package");
}
return processCoordinator; return processCoordinator;
} }
/*static*/ ProcessCoordinator* /*static*/ ProcessCoordinator*
ProcessCoordinatorFactory::_CreateOpenPackageActionCoordinator( ProcessCoordinatorFactory::_CreateOpenPackageActionCoordinator(Model* model, BMessage* message)
Model* model, BMessage* message)
{ {
ProcessCoordinator* processCoordinator = new ProcessCoordinator( ProcessCoordinator* processCoordinator
"OpenPackage", new BMessage(MSG_PACKAGE_ACTION_DONE)); = new ProcessCoordinator("OpenPackage", new BMessage(MSG_PACKAGE_ACTION_DONE));
PackageInfoRef package = _ExtractPackageFromMessage(model, message);
if (package.IsSet()) { BMessage deskbarLinkMessage;
BMessage deskbarLinkMessage;
if (message->FindMessage(KEY_DESKBAR_LINK, &deskbarLinkMessage) != B_OK) if (message->FindMessage(KEY_DESKBAR_LINK, &deskbarLinkMessage) != B_OK)
HDFATAL("malformed message missing key [%s]", KEY_DESKBAR_LINK); HDFATAL("malformed message missing key [%s]", KEY_DESKBAR_LINK);
DeskbarLink deskbarLink(&deskbarLinkMessage);
AbstractProcessNode *processNode = DeskbarLink deskbarLink(&deskbarLinkMessage);
new ThreadedProcessNode(new OpenPackageProcess(
package, model, deskbarLink)); AbstractProcessNode* processNode = new ThreadedProcessNode(
processCoordinator->AddNode(processNode); new OpenPackageProcess(_ExtractPackageNameFromMessage(message), model, deskbarLink));
} else {
HDERROR("unable to find the package"); processCoordinator->AddNode(processNode);
}
return processCoordinator; return processCoordinator;
} }
@@ -269,8 +246,7 @@ ProcessCoordinatorFactory::_CalculateServerProcessOptions()
uint32 processOptions = 0; uint32 processOptions = 0;
if (ServerSettings::IsClientTooOld()) { if (ServerSettings::IsClientTooOld()) {
HDINFO("bulk load proceeding without network communications " HDINFO("bulk load proceeding without network communications because the client is too old");
"because the client is too old");
processOptions |= SERVER_PROCESS_NO_NETWORKING; processOptions |= SERVER_PROCESS_NO_NETWORKING;
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2024, Andrew Lindesay <[email protected]>. * Copyright 2018-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef PROCESS_COORDINATOR_FACTORY_H #ifndef PROCESS_COORDINATOR_FACTORY_H
@@ -26,13 +26,10 @@ public:
static ProcessCoordinator* CreateIncrementViewCounter(Model* model, static ProcessCoordinator* CreateIncrementViewCounter(Model* model,
const PackageInfoRef package); const PackageInfoRef package);
static ProcessCoordinator* CreateBulkLoadCoordinator( static ProcessCoordinator* CreateBulkLoadCoordinator(Model* model, bool forceLocalUpdate);
PackageInfoListenerRef packageInfoListener,
Model* model, bool forceLocalUpdate);
static ProcessCoordinator* CreateUserDetailVerifierCoordinator( static ProcessCoordinator* CreateUserDetailVerifierCoordinator(
UserDetailVerifierListener* UserDetailVerifierListener* userDetailVerifierListener,
userDetailVerifierListener,
Model* model); Model* model);
static ProcessCoordinator* CreatePackageActionCoordinator( static ProcessCoordinator* CreatePackageActionCoordinator(
@@ -42,16 +39,15 @@ public:
Model* model, ScreenshotCoordinate& screenshotCoordinate); Model* model, ScreenshotCoordinate& screenshotCoordinate);
static ProcessCoordinator* PopulatePkgChangelogCoordinator(Model* model, static ProcessCoordinator* PopulatePkgChangelogCoordinator(Model* model,
PackageInfoRef package); const BString& packageName);
static ProcessCoordinator* PopulatePkgUserRatingsCoordinator(Model* model, static ProcessCoordinator* PopulatePkgUserRatingsCoordinator(Model* model,
PackageInfoRef package); const BString& packageName);
private: private:
static uint32 _CalculateServerProcessOptions(); static uint32 _CalculateServerProcessOptions();
static PackageInfoRef _ExtractPackageFromMessage( static BString _ExtractPackageNameFromMessage(BMessage* message);
Model* model, BMessage* message);
static ProcessCoordinator* _CreateInstallPackageActionCoordinator( static ProcessCoordinator* _CreateInstallPackageActionCoordinator(
Model* model, BMessage* message); Model* model, BMessage* message);
@@ -1,17 +1,18 @@
/* /*
* Copyright 2017-2023, Andrew Lindesay <[email protected]>. * Copyright 2017-2025, Andrew Lindesay <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#include "AbstractServerProcess.h" #include "AbstractServerProcess.h"
#include <unistd.h>
#include <errno.h> #include <errno.h>
#include <math.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include <BufferedDataIO.h>
#include <AutoDeleter.h> #include <AutoDeleter.h>
#include <BufferedDataIO.h>
#include <File.h> #include <File.h>
#include <FileIO.h> #include <FileIO.h>
#include <HttpTime.h> #include <HttpTime.h>
@@ -26,7 +27,6 @@
#include "ServerSettings.h" #include "ServerSettings.h"
#include "StandardMetaDataJsonEventListener.h" #include "StandardMetaDataJsonEventListener.h"
#include "StorageUtils.h" #include "StorageUtils.h"
#include "LoggingUrlProtocolListener.h"
using namespace BPrivate::Network; using namespace BPrivate::Network;
@@ -40,14 +40,173 @@ using namespace BPrivate::Network;
#define TIMEOUT_MICROSECONDS 3e+7 #define TIMEOUT_MICROSECONDS 3e+7
const size_t kFileBufferSize = 10 * 1024; static const size_t kFileBufferSize = 10 * 1024;
// #pragma mark - Interface ReadProgressDataIO
class ReadProgressDataIO : public BDataIO
{
public:
ReadProgressDataIO(AbstractServerProcess* serverProcess,
BDataIO* delegate, size_t bytesTotal);
virtual ~ReadProgressDataIO();
virtual ssize_t Read(void* buffer, size_t size);
virtual ssize_t Write(const void* buffer, size_t size);
virtual status_t Flush();
private:
AbstractServerProcess*
fServerProcess;
BDataIO* fDelegate;
size_t fBytesRead;
size_t fBytesTotal;
};
// #pragma mark - Interface LoggingUrlProtocolListener
class LoggingUrlProtocolListener : public BUrlProtocolListener
{
public:
LoggingUrlProtocolListener(AbstractServerProcess* serverProcess,
BString traceLoggingIdentifier, bool traceLogging);
virtual ~LoggingUrlProtocolListener();
size_t ContentLength();
virtual void DownloadProgress(BUrlRequest* caller,
off_t bytesReceived, off_t bytesTotal);
virtual void BytesWritten(BUrlRequest* caller,
size_t bytesWritten);
virtual void DebugMessage(BUrlRequest* caller,
BUrlProtocolDebugMessage type,
const char* text);
private:
AbstractServerProcess*
fServerProcess;
bool fTraceLogging;
BString fTraceLoggingIdentifier;
size_t fContentLength;
};
// #pragma mark - Implementation for ReadProgressDataIO
ReadProgressDataIO::ReadProgressDataIO(AbstractServerProcess* serverProcess, BDataIO* delegate,
size_t bytesTotal)
:
fServerProcess(serverProcess),
fDelegate(delegate),
fBytesRead(0),
fBytesTotal(bytesTotal)
{
}
ReadProgressDataIO::~ReadProgressDataIO()
{
}
ssize_t
ReadProgressDataIO::Read(void* buffer, size_t size)
{
ssize_t actualRead = fDelegate->Read(buffer, size);
if (actualRead > 0) {
fBytesRead += actualRead;
if (fBytesTotal > 0.0f) {
float progress = static_cast<float>(fBytesRead) / static_cast<float>(fBytesTotal);
fServerProcess->SetDataProcessingProgress(progress);
}
}
return actualRead;
}
ssize_t
ReadProgressDataIO::Write(const void* buffer, size_t size)
{
return B_NOT_SUPPORTED;
}
status_t
ReadProgressDataIO::Flush()
{
return B_OK;
}
// #pragma mark - Implementation for LoggingUrlProtocolListener
LoggingUrlProtocolListener::LoggingUrlProtocolListener(AbstractServerProcess* serverProcess,
BString traceLoggingIdentifier, bool traceLogging)
:
fServerProcess(serverProcess),
fTraceLogging(traceLogging),
fTraceLoggingIdentifier(traceLoggingIdentifier),
fContentLength(0)
{
}
LoggingUrlProtocolListener::~LoggingUrlProtocolListener()
{
}
void
LoggingUrlProtocolListener::BytesWritten(BUrlRequest* caller, size_t bytesWritten)
{
fContentLength += bytesWritten;
}
void
LoggingUrlProtocolListener::DownloadProgress(BUrlRequest* caller, off_t bytesReceived,
off_t bytesTotal)
{
float progress = static_cast<float>(bytesReceived) / static_cast<float>(bytesTotal);
fServerProcess->SetDownloadProgress(progress);
}
void
LoggingUrlProtocolListener::DebugMessage(BUrlRequest* caller, BUrlProtocolDebugMessage type,
const char* text)
{
HDTRACE("url->file <%s>; %s", fTraceLoggingIdentifier.String(), text);
}
size_t
LoggingUrlProtocolListener::ContentLength()
{
return fContentLength;
}
// #pragma mark - AbstractServerProcess
AbstractServerProcess::AbstractServerProcess(uint32 options) AbstractServerProcess::AbstractServerProcess(uint32 options)
: :
AbstractProcess(), AbstractProcess(),
fOptions(options), fOptions(options),
fRequest(NULL) fRequest(NULL),
fDownloadProgress(kProgressIndeterminate),
fDataProcessingProgress(kProgressIndeterminate)
{ {
} }
@@ -57,6 +216,44 @@ AbstractServerProcess::~AbstractServerProcess()
} }
float
AbstractServerProcess::Progress()
{
if (fDataProcessingProgress >= 0.0f)
return 0.5f + (0.5f * fminf(fDataProcessingProgress, 1.0));
if (fDownloadProgress >= 0.0f)
return 0.5f * fminf(fDownloadProgress, 1.0);
return kProgressIndeterminate;
}
void
AbstractServerProcess::SetDownloadProgress(float value)
{
if (fDownloadProgress == 1.0 || (!_ShouldProcessProgress() && value != 1.0))
return;
if (fDataProcessingProgress < 0.0f)
fDownloadProgress = value;
else
fDownloadProgress = 1.0;
_NotifyChanged();
}
void
AbstractServerProcess::SetDataProcessingProgress(float value)
{
if (fDataProcessingProgress == 1.0 || (!_ShouldProcessProgress() && value != 1.0))
return;
fDownloadProgress = 1.0;
fDataProcessingProgress = value;
_NotifyChanged();
}
bool bool
AbstractServerProcess::HasOption(uint32 flag) AbstractServerProcess::HasOption(uint32 flag)
{ {
@@ -67,8 +264,7 @@ AbstractServerProcess::HasOption(uint32 flag)
bool bool
AbstractServerProcess::ShouldAttemptNetworkDownload(bool hasDataAlready) AbstractServerProcess::ShouldAttemptNetworkDownload(bool hasDataAlready)
{ {
return return !HasOption(SERVER_PROCESS_NO_NETWORKING)
!HasOption(SERVER_PROCESS_NO_NETWORKING)
&& !(HasOption(SERVER_PROCESS_PREFER_CACHE) && hasDataAlready); && !(HasOption(SERVER_PROCESS_PREFER_CACHE) && hasDataAlready);
} }
@@ -76,16 +272,15 @@ AbstractServerProcess::ShouldAttemptNetworkDownload(bool hasDataAlready)
status_t status_t
AbstractServerProcess::StopInternal() AbstractServerProcess::StopInternal()
{ {
if (fRequest != NULL) { if (fRequest != NULL)
return fRequest->Stop(); return fRequest->Stop();
}
return AbstractProcess::StopInternal(); return AbstractProcess::StopInternal();
} }
status_t status_t
AbstractServerProcess::IfModifiedSinceHeaderValue(BString& headerValue) const AbstractServerProcess::IfModifiedSinceHeaderValue(BString& headerValue)
{ {
BPath metaDataPath; BPath metaDataPath;
BString jsonPath; BString jsonPath;
@@ -102,15 +297,15 @@ AbstractServerProcess::IfModifiedSinceHeaderValue(BString& headerValue) const
status_t status_t
AbstractServerProcess::IfModifiedSinceHeaderValue(BString& headerValue, AbstractServerProcess::IfModifiedSinceHeaderValue(BString& headerValue, const BPath& metaDataPath,
const BPath& metaDataPath, const BString& jsonPath) const const BString& jsonPath)
{ {
headerValue.SetTo(""); headerValue.SetTo("");
struct stat s; struct stat s;
if (-1 == stat(metaDataPath.Path(), &s)) { if (-1 == stat(metaDataPath.Path(), &s)) {
if (ENOENT != errno) if (ENOENT != errno)
return B_ERROR; return B_ERROR;
return B_ENTRY_NOT_FOUND; return B_ENTRY_NOT_FOUND;
} }
@@ -121,11 +316,11 @@ AbstractServerProcess::IfModifiedSinceHeaderValue(BString& headerValue,
StandardMetaData metaData; StandardMetaData metaData;
status_t result = PopulateMetaData(metaData, metaDataPath, jsonPath); status_t result = PopulateMetaData(metaData, metaDataPath, jsonPath);
if (result == B_OK) if (result == B_OK) {
SetIfModifiedSinceHeaderValueFromMetaData(headerValue, metaData); SetIfModifiedSinceHeaderValueFromMetaData(headerValue, metaData);
else { } else {
HDERROR("unable to parse the meta-data date and time from [%s]" HDERROR("unable to parse the meta-data date and time from [%s] - cannot set the "
" - cannot set the 'If-Modified-Since' header", "'If-Modified-Since' header",
metaDataPath.Path()); metaDataPath.Path());
} }
@@ -134,23 +329,19 @@ AbstractServerProcess::IfModifiedSinceHeaderValue(BString& headerValue,
/*static*/ void /*static*/ void
AbstractServerProcess::SetIfModifiedSinceHeaderValueFromMetaData( AbstractServerProcess::SetIfModifiedSinceHeaderValueFromMetaData(BString& headerValue,
BString& headerValue,
const StandardMetaData& metaData) const StandardMetaData& metaData)
{ {
// An example of this output would be; 'Fri, 24 Oct 2014 19:32:27 +0000' // An example of this output would be; 'Fri, 24 Oct 2014 19:32:27 +0000'
BDateTime modifiedDateTime = metaData BDateTime modifiedDateTime = metaData.GetDataModifiedTimestampAsDateTime();
.GetDataModifiedTimestampAsDateTime();
BHttpTime modifiedHttpTime(modifiedDateTime); BHttpTime modifiedHttpTime(modifiedDateTime);
headerValue.SetTo(modifiedHttpTime headerValue.SetTo(modifiedHttpTime.ToString(B_HTTP_TIME_FORMAT_COOKIE));
.ToString(B_HTTP_TIME_FORMAT_COOKIE));
} }
status_t status_t
AbstractServerProcess::PopulateMetaData( AbstractServerProcess::PopulateMetaData(StandardMetaData& metaData, const BPath& path,
StandardMetaData& metaData, const BPath& path, const BString& jsonPath)
const BString& jsonPath) const
{ {
StandardMetaDataJsonEventListener listener(jsonPath, metaData); StandardMetaDataJsonEventListener listener(jsonPath, metaData);
status_t result = ParseJsonFromFileWithListener(&listener, path); status_t result = ParseJsonFromFileWithListener(&listener, path);
@@ -164,8 +355,7 @@ AbstractServerProcess::PopulateMetaData(
return result; return result;
if (!metaData.IsPopulated()) { if (!metaData.IsPopulated()) {
HDERROR("the meta data was read from [%s], but no values " HDERROR("the meta data was read from [%s], but no values were extracted", path.Path());
"were extracted", path.Path());
return B_BAD_DATA; return B_BAD_DATA;
} }
@@ -174,7 +364,7 @@ AbstractServerProcess::PopulateMetaData(
/* static */ bool /* static */ bool
AbstractServerProcess::_LooksLikeGzip(const char *pathStr) AbstractServerProcess::_LooksLikeGzip(const char* pathStr)
{ {
int l = strlen(pathStr); int l = strlen(pathStr);
return l > 4 && 0 == strncmp(&pathStr[l - 3], ".gz", 3); return l > 4 && 0 == strncmp(&pathStr[l - 3], ".gz", 3);
@@ -187,36 +377,53 @@ AbstractServerProcess::_LooksLikeGzip(const char *pathStr)
*/ */
status_t status_t
AbstractServerProcess::ParseJsonFromFileWithListener( AbstractServerProcess::ParseJsonFromFileWithListener(BJsonEventListener* listener,
BJsonEventListener *listener, const BPath& path)
const BPath& path) const
{ {
const char* pathStr = path.Path(); const char* pathStr = path.Path();
bool fileExists = false;
off_t fileSize = 0;
status_t existsResult = StorageUtils::ExistsObject(path, &fileExists, NULL, &fileSize);
if (existsResult != B_OK) {
HDERROR("[%s] unable to get the length of the data file [%s]", Name(), pathStr);
return existsResult;
}
if (!fileExists) {
HDERROR("[%s] missing data file [%s]", Name(), pathStr);
return B_ENTRY_NOT_FOUND;
}
if (fileSize == 0) {
HDERROR("[%s] empty data file [%s]", Name(), pathStr);
return B_BAD_DATA;
}
FILE* file = fopen(pathStr, "rb"); FILE* file = fopen(pathStr, "rb");
if (file == NULL) { if (file == NULL) {
HDERROR("[%s] unable to find the meta data file at [%s]", Name(), HDERROR("[%s] unable to find the data file at [%s]", Name(), pathStr);
path.Path());
return B_ENTRY_NOT_FOUND; return B_ENTRY_NOT_FOUND;
} }
BFileIO rawInput(file, true); // takes ownership BFileIO rawInput(file, true); // takes ownership
ReadProgressDataIO readProgress(this, &rawInput, fileSize);
BDataIO* bufferedRawInput = new BBufferedDataIO(rawInput, kFileBufferSize, false, true); BDataIO* bufferedRawInput = new BBufferedDataIO(readProgress, kFileBufferSize, false, true);
ObjectDeleter<BDataIO> bufferedRawInputDeleter(bufferedRawInput); ObjectDeleter<BDataIO> bufferedRawInputDeleter(bufferedRawInput);
// if the file extension ends with '.gz' then the data will be // if the file extension ends with '.gz' then the data will be
// compressed and the algorithm needs to decompress the data as // compressed and the algorithm needs to decompress the data as
// it is parsed. // it is parsed.
if (_LooksLikeGzip(pathStr)) { if (_LooksLikeGzip(pathStr)) {
BDataIO* gzDecompressedInput = NULL; BDataIO* gzDecompressedInput = NULL;
BZlibDecompressionParameters* zlibDecompressionParameters BZlibDecompressionParameters* zlibDecompressionParameters
= new BZlibDecompressionParameters(); = new BZlibDecompressionParameters();
status_t result = BZlibCompressionAlgorithm() status_t result = BZlibCompressionAlgorithm().CreateDecompressingInputStream(
.CreateDecompressingInputStream(bufferedRawInput, bufferedRawInput, zlibDecompressionParameters, gzDecompressedInput);
zlibDecompressionParameters, gzDecompressedInput);
if (B_OK != result) if (B_OK != result)
return result; return result;
@@ -231,49 +438,47 @@ AbstractServerProcess::ParseJsonFromFileWithListener(
} }
/*! In order to reduce the chance of failure half way through downloading a /*! In order to reduce the chance of failure half way through downloading a
large file, this method will download the file to a temporary file and large file, this method will download the file to a temporary file and
then it can rename the file to the final target file. then it can rename the file to the final target file.
*/ */
status_t status_t
AbstractServerProcess::DownloadToLocalFileAtomically( AbstractServerProcess::DownloadToLocalFileAtomically(const BPath& targetFilePath, const BUrl& url)
const BPath& targetFilePath,
const BUrl& url)
{ {
BPath temporaryFilePath(tmpnam(NULL), NULL, true); BPath temporaryFilePath(tmpnam(NULL), NULL, true);
status_t result = DownloadToLocalFile( status_t result = DownloadToLocalFile(temporaryFilePath, url, 0, 0);
temporaryFilePath, url, 0, 0);
// if the data is coming in as .gz, but is not stored as .gz then // if the data is coming in as .gz, but is not stored as .gz then
// the data should be decompressed in the temporary file location // the data should be decompressed in the temporary file location
// before being shifted into place. // before being shifted into place.
if (result == B_OK if (result == B_OK && _LooksLikeGzip(url.Path()) && !_LooksLikeGzip(targetFilePath.Path()))
&& _LooksLikeGzip(url.Path())
&& !_LooksLikeGzip(targetFilePath.Path()))
result = _DeGzipInSitu(temporaryFilePath); result = _DeGzipInSitu(temporaryFilePath);
// not copying if the data has not changed because the data will be // not copying if the data has not changed because the data will be
// zero length. This is if the result is APP_ERR_NOT_MODIFIED. // zero length. This is if the result is APP_ERR_NOT_MODIFIED.
if (result == B_OK) { if (result == B_OK) {
// if the file is zero length then assume that something has // if the file is zero length then assume that something has
// gone wrong. // gone wrong.
off_t size; off_t size;
bool hasFile; bool hasFile;
result = StorageUtils::ExistsObject(temporaryFilePath, &hasFile, NULL, result = StorageUtils::ExistsObject(temporaryFilePath, &hasFile, NULL, &size);
&size);
if (result == B_OK && hasFile && size > 0) { if (result == B_OK && hasFile && size > 0) {
if (rename(temporaryFilePath.Path(), targetFilePath.Path()) != 0) { if (rename(temporaryFilePath.Path(), targetFilePath.Path()) != 0) {
HDINFO("[%s] did rename [%s] --> [%s]", HDINFO("[%s] did rename [%s] --> [%s]", Name(), temporaryFilePath.Path(),
Name(), temporaryFilePath.Path(), targetFilePath.Path()); targetFilePath.Path());
result = B_IO_ERROR; result = B_IO_ERROR;
} }
} }
} }
if (IsSuccess(result))
SetDownloadProgress(1.0);
return result; return result;
} }
@@ -292,13 +497,11 @@ AbstractServerProcess::_DeGzipInSitu(const BPath& path)
BZlibDecompressionParameters* zlibDecompressionParameters BZlibDecompressionParameters* zlibDecompressionParameters
= new BZlibDecompressionParameters(); = new BZlibDecompressionParameters();
result = BZlibCompressionAlgorithm() result = BZlibCompressionAlgorithm().CreateDecompressingInputStream(&file,
.CreateDecompressingInputStream(&file, zlibDecompressionParameters, gzDecompressedInput);
zlibDecompressionParameters, gzDecompressedInput);
if (result == B_OK) { if (result == B_OK) {
ObjectDeleter<BDataIO> gzDecompressedInputDeleter( ObjectDeleter<BDataIO> gzDecompressedInputDeleter(gzDecompressedInput);
gzDecompressedInput);
result = DataIOUtils::CopyAll(&tmpFile, gzDecompressedInput); result = DataIOUtils::CopyAll(&tmpFile, gzDecompressedInput);
} }
} }
@@ -308,8 +511,7 @@ AbstractServerProcess::_DeGzipInSitu(const BPath& path)
HDERROR("unable to move the uncompressed data into place"); HDERROR("unable to move the uncompressed data into place");
result = B_ERROR; result = B_ERROR;
} }
} } else {
else {
HDERROR("it was not possible to decompress the data"); HDERROR("it was not possible to decompress the data");
} }
@@ -318,15 +520,14 @@ AbstractServerProcess::_DeGzipInSitu(const BPath& path)
status_t status_t
AbstractServerProcess::DownloadToLocalFile(const BPath& targetFilePath, AbstractServerProcess::DownloadToLocalFile(const BPath& targetFilePath, const BUrl& url,
const BUrl& url, uint32 redirects, uint32 failures) uint32 redirects, uint32 failures)
{ {
if (WasStopped()) if (WasStopped())
return B_CANCELED; return B_CANCELED;
if (redirects > MAX_REDIRECTS) { if (redirects > MAX_REDIRECTS) {
HDINFO("[%s] exceeded %d redirects --> failure", Name(), HDINFO("[%s] exceeded %d redirects --> failure", Name(), MAX_REDIRECTS);
MAX_REDIRECTS);
return B_IO_ERROR; return B_IO_ERROR;
} }
@@ -338,7 +539,7 @@ AbstractServerProcess::DownloadToLocalFile(const BPath& targetFilePath,
HDINFO("[%s] will stream '%s' to [%s]", Name(), url.UrlString().String(), HDINFO("[%s] will stream '%s' to [%s]", Name(), url.UrlString().String(),
targetFilePath.Path()); targetFilePath.Path());
LoggingUrlProtocolListener listener(Name(), Logger::IsTraceEnabled()); LoggingUrlProtocolListener listener(this, Name(), Logger::IsTraceEnabled());
BFile targetFile(targetFilePath.Path(), O_WRONLY | O_CREAT); BFile targetFile(targetFilePath.Path(), O_WRONLY | O_CREAT);
status_t err = targetFile.InitCheck(); status_t err = targetFile.InitCheck();
if (err != B_OK) if (err != B_OK)
@@ -348,26 +549,23 @@ AbstractServerProcess::DownloadToLocalFile(const BPath& targetFilePath,
ServerSettings::AugmentHeaders(headers); ServerSettings::AugmentHeaders(headers);
BString ifModifiedSinceHeader; BString ifModifiedSinceHeader;
status_t ifModifiedSinceHeaderStatus = IfModifiedSinceHeaderValue( status_t ifModifiedSinceHeaderStatus = IfModifiedSinceHeaderValue(ifModifiedSinceHeader);
ifModifiedSinceHeader);
if (ifModifiedSinceHeaderStatus == B_OK && if (ifModifiedSinceHeaderStatus == B_OK && ifModifiedSinceHeader.Length() > 0)
ifModifiedSinceHeader.Length() > 0) {
headers.AddHeader("If-Modified-Since", ifModifiedSinceHeader); headers.AddHeader("If-Modified-Since", ifModifiedSinceHeader);
}
thread_id thread; thread_id thread;
BUrlRequest* request = BUrlProtocolRoster::MakeRequest(url, &targetFile, BUrlRequest* request = BUrlProtocolRoster::MakeRequest(url, &targetFile, &listener);
&listener);
if (request == NULL) if (request == NULL)
return B_NO_MEMORY; return B_NO_MEMORY;
fRequest = dynamic_cast<BHttpRequest *>(request); fRequest = dynamic_cast<BHttpRequest*>(request);
if (fRequest == NULL) { if (fRequest == NULL) {
delete request; delete request;
return B_ERROR; return B_ERROR;
} }
fRequest->SetHeaders(headers); fRequest->SetHeaders(headers);
fRequest->SetMaxRedirections(0); fRequest->SetMaxRedirections(0);
fRequest->SetTimeout(TIMEOUT_MICROSECONDS); fRequest->SetTimeout(TIMEOUT_MICROSECONDS);
@@ -376,11 +574,10 @@ AbstractServerProcess::DownloadToLocalFile(const BPath& targetFilePath,
wait_for_thread(thread, NULL); wait_for_thread(thread, NULL);
const BHttpResult& result = dynamic_cast<const BHttpResult&>( const BHttpResult& result = dynamic_cast<const BHttpResult&>(fRequest->Result());
fRequest->Result());
int32 statusCode = result.StatusCode(); int32 statusCode = result.StatusCode();
const BHttpHeaders responseHeaders = result.Headers(); const BHttpHeaders responseHeaders = result.Headers();
const char *locationC = responseHeaders["Location"]; const char* locationC = responseHeaders["Location"];
BString location; BString location;
if (locationC != NULL) if (locationC != NULL)
@@ -415,8 +612,7 @@ AbstractServerProcess::DownloadToLocalFile(const BPath& targetFilePath,
return DownloadToLocalFile(targetFilePath, url, redirects, failures + 1); return DownloadToLocalFile(targetFilePath, url, redirects, failures + 1);
} }
HDERROR("[%s] unexpected response from server [%" B_PRId32 "]", HDERROR("[%s] unexpected response from server [%" B_PRId32 "]", Name(), statusCode);
Name(), statusCode);
return B_IO_ERROR; return B_IO_ERROR;
} }
} }
@@ -433,9 +629,9 @@ AbstractServerProcess::DeleteLocalFile(const BPath& currentFilePath)
/*! When a file is damaged or corrupted in some way, the file should be 'moved /*! When a file is damaged or corrupted in some way, the file should be 'moved
aside' so that it is not involved in the next update. This method will aside' so that it is not involved in the next update. This method will
create such an alternative 'damaged' file location and move this file to create such an alternative 'damaged' file location and move this file to
that location. that location.
*/ */
status_t status_t
@@ -449,19 +645,20 @@ AbstractServerProcess::MoveDamagedFileAside(const BPath& currentFilePath)
damagedFilePath.Append(damagedLeaf.String()); damagedFilePath.Append(damagedLeaf.String());
if (0 != rename(currentFilePath.Path(), damagedFilePath.Path())) { if (0 != rename(currentFilePath.Path(), damagedFilePath.Path())) {
HDERROR("[%s] unable to move damaged file [%s] aside to [%s]", HDERROR("[%s] unable to move damaged file [%s] aside to [%s]", Name(),
Name(), currentFilePath.Path(), damagedFilePath.Path()); currentFilePath.Path(), damagedFilePath.Path());
return B_IO_ERROR; return B_IO_ERROR;
} }
HDINFO("[%s] did move damaged file [%s] aside to [%s]", HDINFO("[%s] did move damaged file [%s] aside to [%s]", Name(), currentFilePath.Path(),
Name(), currentFilePath.Path(), damagedFilePath.Path()); damagedFilePath.Path());
return B_OK; return B_OK;
} }
bool bool
AbstractServerProcess::IsSuccess(status_t e) { AbstractServerProcess::IsSuccess(status_t e)
{
return e == B_OK || e == HD_ERR_NOT_MODIFIED; return e == B_OK || e == HD_ERR_NOT_MODIFIED;
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright 2017-2020, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2017-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef ABSTRACT_SERVER_PROCESS_H #ifndef ABSTRACT_SERVER_PROCESS_H
@@ -34,6 +34,11 @@ public:
AbstractServerProcess(uint32 options); AbstractServerProcess(uint32 options);
virtual ~AbstractServerProcess(); virtual ~AbstractServerProcess();
virtual float Progress();
void SetDownloadProgress(float value);
void SetDataProcessingProgress(float value);
protected: protected:
virtual status_t GetStandardMetaDataPath( virtual status_t GetStandardMetaDataPath(
BPath& path) const = 0; BPath& path) const = 0;
@@ -41,11 +46,11 @@ protected:
BString& jsonPath) const = 0; BString& jsonPath) const = 0;
virtual status_t IfModifiedSinceHeaderValue( virtual status_t IfModifiedSinceHeaderValue(
BString& headerValue) const; BString& headerValue);
status_t IfModifiedSinceHeaderValue( status_t IfModifiedSinceHeaderValue(
BString& headerValue, BString& headerValue,
const BPath& metaDataPath, const BPath& metaDataPath,
const BString& jsonPath) const; const BString& jsonPath);
static void SetIfModifiedSinceHeaderValueFromMetaData( static void SetIfModifiedSinceHeaderValueFromMetaData(
BString& headerValue, BString& headerValue,
const StandardMetaData& metaData); const StandardMetaData& metaData);
@@ -53,11 +58,11 @@ protected:
status_t PopulateMetaData( status_t PopulateMetaData(
StandardMetaData& metaData, StandardMetaData& metaData,
const BPath& path, const BPath& path,
const BString& jsonPath) const; const BString& jsonPath);
status_t ParseJsonFromFileWithListener( status_t ParseJsonFromFileWithListener(
BJsonEventListener *listener, BJsonEventListener *listener,
const BPath& path) const; const BPath& path);
virtual status_t DownloadToLocalFileAtomically( virtual status_t DownloadToLocalFileAtomically(
const BPath& targetFilePath, const BPath& targetFilePath,
@@ -81,13 +86,16 @@ private:
const BUrl& url, const BUrl& url,
uint32 redirects, uint32 failures); uint32 redirects, uint32 failures);
static bool _LooksLikeGzip(const char *pathStr); static bool _LooksLikeGzip(const char *pathStr);
static status_t _DeGzipInSitu(const BPath& path); static status_t _DeGzipInSitu(const BPath& path);
private: private:
uint32 fOptions; uint32 fOptions;
BHttpRequest* fRequest; BHttpRequest* fRequest;
float fDownloadProgress;
float fDataProcessingProgress;
}; };
#endif // ABSTRACT_SERVER_PROCESS_H #endif // ABSTRACT_SERVER_PROCESS_H
@@ -1,5 +1,5 @@
/* /*
* Copyright 2017-2022, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2017-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
@@ -16,12 +16,11 @@
#include "StorageUtils.h" #include "StorageUtils.h"
AbstractSingleFileServerProcess::AbstractSingleFileServerProcess( AbstractSingleFileServerProcess::AbstractSingleFileServerProcess(uint32 options)
uint32 options)
: :
AbstractServerProcess(options), AbstractServerProcess(options),
fDownloadDurationSeconds(0.0), fDownloadDurationSeconds(0.0),
fProcessLocalDataDurationSeconds(0.0) fProcessLocalDataDurationSeconds(0.0)
{ {
} }
@@ -56,25 +55,25 @@ AbstractSingleFileServerProcess::RunInternal()
if (IsSuccess(result) && ShouldAttemptNetworkDownload(hasData)) { if (IsSuccess(result) && ShouldAttemptNetworkDownload(hasData)) {
BStopWatch stopWatch("download", true); BStopWatch stopWatch("download", true);
result = DownloadToLocalFileAtomically( result = DownloadToLocalFileAtomically(localPath,
localPath,
ServerSettings::CreateFullUrl(urlPathComponent)); ServerSettings::CreateFullUrl(urlPathComponent));
fDownloadDurationSeconds = ((double) stopWatch.ElapsedTime() / 1000000.0); fDownloadDurationSeconds = ((double)stopWatch.ElapsedTime() / 1000000.0);
if (!IsSuccess(result)) { if (!IsSuccess(result)) {
if (hasData) { if (hasData) {
HDINFO("[%s] failed to update data, but have old data " HDINFO("[%s] failed to update data, but have old data anyway so carry on with that",
"anyway so carry on with that", Name()); Name());
result = B_OK; result = B_OK;
} else } else {
HDERROR("[%s] failed to obtain data", Name()); HDERROR("[%s] failed to obtain data", Name());
} else }
} else {
HDINFO("[%s] did fetch data", Name()); HDINFO("[%s] did fetch data", Name());
}
} }
if (IsSuccess(result)) { if (IsSuccess(result)) {
status_t hasDataResult = StorageUtils::ExistsObject( status_t hasDataResult = StorageUtils::ExistsObject(localPath, &hasData, NULL, &size);
localPath, &hasData, NULL, &size);
hasData = hasData && size > 0; hasData = hasData && size > 0;
@@ -89,7 +88,7 @@ AbstractSingleFileServerProcess::RunInternal()
BStopWatch stopWatch("process local data", true); BStopWatch stopWatch("process local data", true);
result = ProcessLocalData(); result = ProcessLocalData();
fProcessLocalDataDurationSeconds = ((double) stopWatch.ElapsedTime() / 1000000.0); fProcessLocalDataDurationSeconds = ((double)stopWatch.ElapsedTime() / 1000000.0);
switch (result) { switch (result) {
case B_OK: case B_OK:
@@ -124,8 +123,7 @@ AbstractSingleFileServerProcess::LogReport()
if (ProcessState() == PROCESS_COMPLETE) { if (ProcessState() == PROCESS_COMPLETE) {
BString downloadLogLine; BString downloadLogLine;
BString localDataLogLine; BString localDataLogLine;
downloadLogLine.SetToFormat("\n - download %6.3f", downloadLogLine.SetToFormat("\n - download %6.3f", fDownloadDurationSeconds);
fDownloadDurationSeconds);
localDataLogLine.SetToFormat("\n - process local data %6.3f", localDataLogLine.SetToFormat("\n - process local data %6.3f",
fProcessLocalDataDurationSeconds); fProcessLocalDataDurationSeconds);
result.Append(downloadLogLine); result.Append(downloadLogLine);
@@ -1,5 +1,5 @@
/* /*
* Copyright 2021-2023, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2021-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#include "IncrementViewCounterProcess.h" #include "IncrementViewCounterProcess.h"
@@ -20,14 +20,14 @@
#define B_TRANSLATION_CONTEXT "IncrementViewCounterProcess" #define B_TRANSLATION_CONTEXT "IncrementViewCounterProcess"
IncrementViewCounterProcess::IncrementViewCounterProcess( IncrementViewCounterProcess::IncrementViewCounterProcess(Model* model,
Model* model, const PackageInfoRef& package) const PackageInfoRef& package)
: :
fPackage(package), fPackage(package),
fModel(model) fModel(model)
{ {
fDescription = BString(B_TRANSLATE("Recording view of \"%PackageName%\"")) fDescription = BString(B_TRANSLATE("Recording view of \"%PackageName%\""))
.ReplaceAll("%PackageName%", fPackage->Name()); .ReplaceAll("%PackageName%", fPackage->Name());
} }
@@ -77,8 +77,8 @@ IncrementViewCounterProcess::RunInternal()
while (attempts > 0 && !WasStopped()) { while (attempts > 0 && !WasStopped()) {
BMessage resultEnvelope; BMessage resultEnvelope;
WebAppInterface* webAppInterface = fModel->GetWebAppInterface(); WebAppInterfaceRef webApp = fModel->WebApp();
result = webAppInterface->IncrementViewCounter(fPackage, depot, resultEnvelope); result = webApp->IncrementViewCounter(fPackage, depot, resultEnvelope);
if (result == B_OK) { if (result == B_OK) {
int32 errorCode = WebAppInterface::ErrorCodeFromResponse(resultEnvelope); int32 errorCode = WebAppInterface::ErrorCodeFromResponse(resultEnvelope);
@@ -97,8 +97,9 @@ IncrementViewCounterProcess::RunInternal()
result = B_ERROR; result = B_ERROR;
break; break;
} }
} else } else {
HDERROR("an error has arisen incrementing the view counter"); HDERROR("an error has arisen incrementing the view counter");
}
attempts--; attempts--;
_SpinBetweenAttempts(); _SpinBetweenAttempts();
@@ -115,4 +116,3 @@ IncrementViewCounterProcess::_SpinBetweenAttempts()
for (int32 i = 0; i < 10 && !WasStopped(); i++) for (int32 i = 0; i < 10 && !WasStopped(); i++)
usleep(miniSpinDelays); usleep(miniSpinDelays);
} }
File diff suppressed because it is too large Load Diff
@@ -1,11 +1,13 @@
/* /*
* Copyright 2018-2022, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2018-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef LOCAL_PKG_DATA_LOAD_PROCESS__H #ifndef LOCAL_PKG_DATA_LOAD_PROCESS__H
#define LOCAL_PKG_DATA_LOAD_PROCESS__H #define LOCAL_PKG_DATA_LOAD_PROCESS__H
#include <map>
#include "AbstractProcess.h" #include "AbstractProcess.h"
#include "Model.h" #include "Model.h"
@@ -29,9 +31,7 @@ class PkgDataLoadState;
class LocalPkgDataLoadProcess : public AbstractProcess { class LocalPkgDataLoadProcess : public AbstractProcess {
public: public:
LocalPkgDataLoadProcess( LocalPkgDataLoadProcess(Model *model, bool force = false);
PackageInfoListenerRef packageInfoListener,
Model *model, bool force = false);
virtual ~LocalPkgDataLoadProcess(); virtual ~LocalPkgDataLoadProcess();
const char* Name() const; const char* Name() const;
@@ -41,14 +41,11 @@ protected:
virtual status_t RunInternal(); virtual status_t RunInternal();
private: private:
void _NotifyError(const BString& messageText) const; static void _NotifyError(const BString& messageText);
off_t _DeriveSize(const PackageInfoRef modelInfo) const;
private: private:
Model* fModel; Model* fModel;
bool fForce; bool fForce;
PackageInfoListenerRef
fPackageInfoListener;
}; };
#endif // LOCAL_PKG_DATA_LOAD_PROCESS__H #endif // LOCAL_PKG_DATA_LOAD_PROCESS__H
@@ -1,5 +1,5 @@
/* /*
* Copyright 2016-2024, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2016-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
* *
* Note that this file included code earlier from `Model.cpp` and * Note that this file included code earlier from `Model.cpp` and
@@ -21,10 +21,10 @@
PopulatePkgChangelogFromServerProcess::PopulatePkgChangelogFromServerProcess( PopulatePkgChangelogFromServerProcess::PopulatePkgChangelogFromServerProcess(
PackageInfoRef packageInfo, Model* model) const BString& packageName, Model* model)
: :
fModel(model), fModel(model),
fPackageInfo(packageInfo) fPackageName(packageName)
{ {
} }
@@ -54,10 +54,9 @@ PopulatePkgChangelogFromServerProcess::RunInternal()
// TODO; use API spec to code generation techniques instead of this manually written client. // TODO; use API spec to code generation techniques instead of this manually written client.
BMessage responsePayload; BMessage responsePayload;
BString packageName = fPackageInfo->Name();
status_t result; status_t result;
result = fModel->GetWebAppInterface()->GetChangelog(packageName, responsePayload); result = fModel->WebApp()->GetChangelog(fPackageName, responsePayload);
if (result == B_OK) { if (result == B_OK) {
BMessage resultMessage; BMessage resultMessage;
@@ -67,31 +66,11 @@ PopulatePkgChangelogFromServerProcess::RunInternal()
if (result == B_OK) { if (result == B_OK) {
if (resultMessage.FindString("content", &content) == B_OK) { if (resultMessage.FindString("content", &content) == B_OK) {
content.Trim(); result = _UpdateChangelog(content.Trim());
HDDEBUG("changelog populated for [%s]", fPackageName.String());
PackageLocalizedTextRef localizedText = fPackageInfo->LocalizedText();
if (localizedText.IsSet()) {
localizedText = PackageLocalizedTextRef(
new PackageLocalizedText(*(localizedText.Get())), true);
} else {
localizedText = PackageLocalizedTextRef(new PackageLocalizedText(), true);
}
localizedText->SetChangelog(content);
// TODO; later work to make the `PackageInfo` immutable to avoid this locking
BAutolock locker(fModel->Lock());
fPackageInfo->SetLocalizedText(localizedText);
HDDEBUG("changelog populated for [%s]", packageName.String());
} else { } else {
HDDEBUG("no changelog present for [%s]", packageName.String()); HDDEBUG("no changelog present for [%s]", fPackageName.String());
} }
} else {
int32 errorCode = WebAppInterface::ErrorCodeFromResponse(responsePayload);
if (errorCode != ERROR_CODE_NONE)
ServerHelper::NotifyServerJsonRpcError(responsePayload);
} }
} else { } else {
ServerHelper::NotifyTransportError(result); ServerHelper::NotifyTransportError(result);
@@ -99,3 +78,25 @@ PopulatePkgChangelogFromServerProcess::RunInternal()
return result; return result;
} }
status_t
PopulatePkgChangelogFromServerProcess::_UpdateChangelog(const BString& value)
{
PackageInfoRef package = fModel->PackageForName(fPackageName);
if (!package.IsSet()) {
HDERROR("the package [%s] was not found", fPackageName.String());
return B_ERROR;
}
PackageLocalizedTextRef localizedText
= PackageLocalizedTextBuilder(package->LocalizedText()).WithChangelog(value).BuildRef();
PackageInfoRef updatedPackage
= PackageInfoBuilder(package).WithLocalizedText(localizedText).BuildRef();
fModel->AddPackage(updatedPackage);
return B_OK;
}
@@ -1,5 +1,5 @@
/* /*
* Copyright 2024, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2024-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
@@ -31,7 +31,7 @@ class PkgDataLoadState;
class PopulatePkgChangelogFromServerProcess : public AbstractProcess class PopulatePkgChangelogFromServerProcess : public AbstractProcess
{ {
public: public:
PopulatePkgChangelogFromServerProcess(PackageInfoRef packageInfo, PopulatePkgChangelogFromServerProcess(const BString& packageName,
Model *model); Model *model);
virtual ~PopulatePkgChangelogFromServerProcess(); virtual ~PopulatePkgChangelogFromServerProcess();
@@ -41,9 +41,12 @@ public:
protected: protected:
virtual status_t RunInternal(); virtual status_t RunInternal();
private:
status_t _UpdateChangelog(const BString& value);
private: private:
Model* fModel; Model* fModel;
PackageInfoRef fPackageInfo; BString fPackageName;
}; };
#endif // POPULATE_PKG_CHANGELOG_FROM_SERVER_PROCESS__H #endif // POPULATE_PKG_CHANGELOG_FROM_SERVER_PROCESS__H
@@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2024, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2018-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
* *
@@ -23,7 +23,8 @@
PopulatePkgSizesProcess::PopulatePkgSizesProcess(Model* model) PopulatePkgSizesProcess::PopulatePkgSizesProcess(Model* model)
: :
fModel(model) fModel(model),
fProgress(0)
{ {
} }
@@ -47,55 +48,97 @@ PopulatePkgSizesProcess::Description() const
} }
float
PopulatePkgSizesProcess::Progress()
{
return fProgress;
}
status_t status_t
PopulatePkgSizesProcess::RunInternal() PopulatePkgSizesProcess::RunInternal()
{ {
int32 countPkgs = 0; int32 countPkgs;
int32 countPkgSized = 0; int32 countPkgSized = 0;
int32 countPkgUnsized = 0; int32 countPkgUnsized = 0;
HDINFO("[%s] will populate size for pkgs without a size", Name()); HDINFO("[%s] will populate size for pkgs without a size", Name());
for (int32 d = 0; d < fModel->CountDepots() && !WasStopped(); d++) { const std::vector<PackageInfoRef> packages = _SnapshotOfPackages();
DepotInfoRef depotInfo = fModel->DepotAtIndex(d); std::vector<PackageInfoRef> updatedPackages;
countPkgs += depotInfo->CountPackages();
for (int32 p = 0; p < depotInfo->CountPackages(); p++) { countPkgs = static_cast<int32>(packages.size());
PackageInfoRef packageInfo = depotInfo->PackageAtIndex(p);
if (!packageInfo.IsSet()) for (int32 i = 0; i < countPkgs; i++) {
HDFATAL("package is not set"); PackageInfoRef package = packages[i];
const char* packageName = package->Name().String();
PackageLocalInfoRef localInfo = PackageUtils::NewLocalInfo(packageInfo); if (!package.IsSet())
PackageState state = localInfo->State(); HDFATAL("package is not set");
if (localInfo->Size() <= 0 && (state == ACTIVATED || state == INSTALLED)) { PackageLocalInfoRef localInfo = package->LocalInfo();
off_t derivedSize = _DeriveSize(packageInfo);
if (derivedSize > 0) { if (_ShouldDeriveSize(localInfo)) {
localInfo->SetSize(derivedSize); off_t derivedSize = _DeriveSize(package);
countPkgSized++;
HDDEBUG("[%s] did derive a size for package [%s]", Name(), if (derivedSize > 0) {
packageInfo->Name().String()); PackageLocalInfoBuilder localInfoBuilder;
} else {
countPkgUnsized++; if (localInfo.IsSet())
HDDEBUG("[%s] unable to derive a size for package [%s]", Name(), localInfoBuilder = PackageLocalInfoBuilder(localInfo);
packageInfo->Name().String());
} localInfoBuilder.WithSize(derivedSize);
PackageLocalInfoRef localInfo = localInfoBuilder.BuildRef();
updatedPackages.push_back(
PackageInfoBuilder(package).WithLocalInfo(localInfo).BuildRef());
countPkgSized++;
HDDEBUG("[%s] did derive a size for package [%s]", Name(), packageName);
} else {
countPkgUnsized++;
HDDEBUG("[%s] unable to derive a size for package [%s]", Name(), packageName);
} }
packageInfo->SetLocalInfo(localInfo);
} }
_SetProgress(static_cast<float>(i) / static_cast<float>(countPkgs));
} }
fModel->AddPackagesWithChange(updatedPackages, PKG_CHANGED_LOCAL_INFO);
_SetProgress(1.0);
HDINFO("[%s] did populate size for %" B_PRId32 " packages with %" B_PRId32 HDINFO("[%s] did populate size for %" B_PRId32 " packages with %" B_PRId32
" already having a size and %" B_PRId32 " unable to derive a size", " already having a size and %" B_PRId32 " unable to derive a size",
Name(), countPkgSized, countPkgs - countPkgSized, countPkgUnsized); Name(), countPkgSized, countPkgs - (countPkgSized + countPkgUnsized), countPkgUnsized);
return B_OK; return B_OK;
} }
const std::vector<PackageInfoRef>
PopulatePkgSizesProcess::_SnapshotOfPackages() const
{
return fModel->Packages();
}
bool
PopulatePkgSizesProcess::_ShouldDeriveSize(PackageLocalInfoRef localInfo) const
{
if (!localInfo.IsSet())
return true;
if (localInfo->Size() > 0)
return false;
PackageState state = localInfo->State();
return state == ACTIVATED || state == INSTALLED;
}
off_t off_t
PopulatePkgSizesProcess::_DeriveSize(const PackageInfoRef package) const PopulatePkgSizesProcess::_DeriveSize(const PackageInfoRef package) const
{ {
@@ -111,4 +154,14 @@ PopulatePkgSizesProcess::_DeriveSize(const PackageInfoRef package) const
HDDEBUG("unable to get the local file of package [%s]", package->Name().String()); HDDEBUG("unable to get the local file of package [%s]", package->Name().String());
} }
return 0; return 0;
} }
void
PopulatePkgSizesProcess::_SetProgress(float value)
{
if (!_ShouldProcessProgress() && value != 1.0 && (value - fProgress) < 0.1)
return;
fProgress = value;
_NotifyChanged();
}
@@ -1,10 +1,12 @@
/* /*
* Copyright 2023, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2023-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef POPULATE_PKG_SIZES_PROCESS_H #ifndef POPULATE_PKG_SIZES_PROCESS_H
#define POPULATE_PKG_SIZES_PROCESS_H #define POPULATE_PKG_SIZES_PROCESS_H
#include <vector>
#include <String.h> #include <String.h>
#include "AbstractProcess.h" #include "AbstractProcess.h"
@@ -23,15 +25,22 @@ public:
virtual const char* Name() const; virtual const char* Name() const;
virtual const char* Description() const; virtual const char* Description() const;
virtual float Progress();
protected: protected:
virtual status_t RunInternal(); virtual status_t RunInternal();
private: private:
const std::vector<PackageInfoRef>
_SnapshotOfPackages() const;
bool _ShouldDeriveSize(PackageLocalInfoRef localInfo) const;
off_t _DeriveSize(const PackageInfoRef modelInfo) const; off_t _DeriveSize(const PackageInfoRef modelInfo) const;
void _SetProgress(float value);
private: private:
Model* fModel; Model* fModel;
float fProgress;
}; };
@@ -1,5 +1,5 @@
/* /*
* Copyright 2016-2024, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2016-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>. * Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
* *
@@ -23,10 +23,10 @@
PopulatePkgUserRatingsFromServerProcess::PopulatePkgUserRatingsFromServerProcess( PopulatePkgUserRatingsFromServerProcess::PopulatePkgUserRatingsFromServerProcess(
PackageInfoRef packageInfo, Model* model) const BString& packageName, Model* model)
: :
fModel(model), fModel(model),
fPackageInfo(packageInfo) fPackageName(packageName)
{ {
} }
@@ -59,29 +59,21 @@ PopulatePkgUserRatingsFromServerProcess::RunInternal()
// Retrieve info from web-app // Retrieve info from web-app
BMessage info; BMessage info;
BString webAppRepositoryCode = _WebAppRepositoryCode();
BString packageName; if (webAppRepositoryCode.IsEmpty()) {
BString webAppRepositoryCode; HDERROR("unable to get the web app repository code for pkg [%s]", fPackageName.String());
status = B_ERROR;
{
BAutolock locker(&fLock);
packageName = fPackageInfo->Name();
BString depotName = PackageUtils::DepotName(fPackageInfo);
const DepotInfo* depot = fModel->DepotForName(depotName);
if (depot != NULL)
webAppRepositoryCode = depot->WebAppRepositoryCode();
} }
if (status == B_OK) { if (status == B_OK) {
status = fModel->GetWebAppInterface()->RetrieveUserRatingsForPackageForDisplay(packageName, status = fModel->WebApp()->RetrieveUserRatingsForPackageForDisplay(fPackageName,
webAppRepositoryCode, BString(), 0, PACKAGE_INFO_MAX_USER_RATINGS, info); webAppRepositoryCode, BString(), 0, PACKAGE_INFO_MAX_USER_RATINGS, info);
// ^ note intentionally not using the repository source code as this would then show // ^ note intentionally not using the repository source code as this would then show
// too few results as it would be architecture specific. // too few results as it would be architecture specific.
} }
UserRatingInfoRef userRatingInfo(new UserRatingInfo(), true); PackageUserRatingInfoBuilder userRatingInfoBuilder;
if (status == B_OK) { if (status == B_OK) {
// Parse message // Parse message
@@ -112,7 +104,7 @@ PopulatePkgUserRatingsFromServerProcess::RunInternal()
BString user; BString user;
BMessage userInfo; BMessage userInfo;
if (item.FindMessage("user", &userInfo) != B_OK if (item.FindMessage("user", &userInfo) != B_OK
|| userInfo.FindString("nickname", &user) != B_OK) { || userInfo.FindString("nickname", &user) != B_OK) {
HDERROR("ignored user rating [%s] without a user nickname", code.String()); HDERROR("ignored user rating [%s] without a user nickname", code.String());
continue; continue;
} }
@@ -170,15 +162,15 @@ PopulatePkgUserRatingsFromServerProcess::RunInternal()
UserRatingRef userRating( UserRatingRef userRating(
new UserRating(UserInfo(user), rating, comment, languageCode, new UserRating(UserInfo(user), rating, comment, languageCode,
// note that language identifiers are "code" in HDS and "id" in Haiku // note that language identifiers are "code" in HDS and "id" in Haiku
versionString, (uint64) createTimestamp), versionString, static_cast<uint64>(createTimestamp)),
true); true);
userRatingInfo->AddUserRating(userRating); userRatingInfoBuilder.AddUserRating(userRating);
HDDEBUG("rating [%s] retrieved from server", code.String()); HDDEBUG("rating [%s] retrieved from server", code.String());
} }
userRatingInfo->SetUserRatingsPopulated(); userRatingInfoBuilder.WithUserRatingsPopulated();
HDDEBUG("did retrieve %" B_PRIi32 " user ratings for [%s]", index - 1, HDDEBUG("did retrieve %" B_PRIi32 " user ratings for [%s]", index - 1,
packageName.String()); fPackageName.String());
} }
} else { } else {
int32 errorCode = WebAppInterface::ErrorCodeFromResponse(info); int32 errorCode = WebAppInterface::ErrorCodeFromResponse(info);
@@ -197,14 +189,13 @@ PopulatePkgUserRatingsFromServerProcess::RunInternal()
BMessage summaryResponse; BMessage summaryResponse;
if (status == B_OK) { if (status == B_OK) {
status = fModel->GetWebAppInterface()->RetrieveUserRatingSummaryForPackage(packageName, status = fModel->WebApp()->RetrieveUserRatingSummaryForPackage(fPackageName,
webAppRepositoryCode, summaryResponse); webAppRepositoryCode, summaryResponse);
} }
if (status == B_OK) { if (status == B_OK) {
// Parse message // Parse message
UserRatingSummaryBuilder userRatingSummaryBuilder;
UserRatingSummaryRef userRatingSummary(new UserRatingSummary(), true);
BMessage result; BMessage result;
@@ -214,15 +205,18 @@ PopulatePkgUserRatingsFromServerProcess::RunInternal()
status = summaryResponse.FindMessage("result", &result); status = summaryResponse.FindMessage("result", &result);
double sampleSizeF; double sampleSizeF;
int sampleSize = 0;
bool hasData; bool hasData;
if (status == B_OK) if (status == B_OK)
status = result.FindDouble("sampleSize", &sampleSizeF); status = result.FindDouble("sampleSize", &sampleSizeF);
if (status == B_OK) if (status == B_OK) {
userRatingSummary->SetRatingCount(static_cast<int>(sampleSizeF)); sampleSize = static_cast<int>(sampleSizeF);
userRatingSummaryBuilder.WithRatingCount(sampleSize);
}
hasData = status == B_OK && userRatingSummary->RatingCount() > 0; hasData = status == B_OK && sampleSize > 0;
if (hasData) { if (hasData) {
double ratingF; double ratingF;
@@ -231,7 +225,7 @@ PopulatePkgUserRatingsFromServerProcess::RunInternal()
status = result.FindDouble("rating", &ratingF); status = result.FindDouble("rating", &ratingF);
if (status == B_OK) if (status == B_OK)
userRatingSummary->SetAverageRating(ratingF); userRatingSummaryBuilder.WithAverageRating(ratingF);
} }
if (hasData) { if (hasData) {
@@ -261,11 +255,12 @@ PopulatePkgUserRatingsFromServerProcess::RunInternal()
if (status == B_OK) if (status == B_OK)
status = ratingDistributionItem.FindDouble("total", &ratingDistributionTotalF); status = ratingDistributionItem.FindDouble("total", &ratingDistributionTotalF);
userRatingSummary->SetRatingByStar(static_cast<int>(ratingDistributionRatingF), userRatingSummaryBuilder.AddRatingByStar(
static_cast<int>(ratingDistributionRatingF),
static_cast<int>(ratingDistributionTotalF)); static_cast<int>(ratingDistributionTotalF));
} }
userRatingInfo->SetSummary(userRatingSummary); userRatingInfoBuilder.WithSummary(userRatingSummaryBuilder.BuildRef());
} else { } else {
int32 errorCode = WebAppInterface::ErrorCodeFromResponse(summaryResponse); int32 errorCode = WebAppInterface::ErrorCodeFromResponse(summaryResponse);
@@ -277,10 +272,28 @@ PopulatePkgUserRatingsFromServerProcess::RunInternal()
} }
if (status == B_OK) { if (status == B_OK) {
// TODO; later make the PackageInfo immutable to avoid the need for locking here. PackageInfoRef package = fModel->PackageForName(fPackageName);
BAutolock locker(&fLock);
fPackageInfo->SetUserRatingInfo(userRatingInfo); PackageInfoRef updatedPackage = PackageInfoBuilder(package)
.WithUserRatingInfo(userRatingInfoBuilder.BuildRef())
.BuildRef();
fModel->AddPackage(updatedPackage);
} }
return status; return status;
} }
const BString
PopulatePkgUserRatingsFromServerProcess::_WebAppRepositoryCode() const
{
const PackageInfoRef package = fModel->PackageForName(fPackageName);
const BString depotName = PackageUtils::DepotName(package);
const DepotInfoRef depot = fModel->DepotForName(depotName);
if (depot.IsSet())
return depot->WebAppRepositoryCode();
return BString();
}
@@ -1,5 +1,5 @@
/* /*
* Copyright 2024, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2024-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef POPULATE_PKG_USER_RATINGS_FROM_SERVER_PROCESS__H #ifndef POPULATE_PKG_USER_RATINGS_FROM_SERVER_PROCESS__H
@@ -29,7 +29,7 @@ class PkgDataLoadState;
class PopulatePkgUserRatingsFromServerProcess : public AbstractProcess class PopulatePkgUserRatingsFromServerProcess : public AbstractProcess
{ {
public: public:
PopulatePkgUserRatingsFromServerProcess(PackageInfoRef packageInfo, PopulatePkgUserRatingsFromServerProcess(const BString& packageName,
Model *model); Model *model);
virtual ~PopulatePkgUserRatingsFromServerProcess(); virtual ~PopulatePkgUserRatingsFromServerProcess();
@@ -39,9 +39,12 @@ public:
protected: protected:
virtual status_t RunInternal(); virtual status_t RunInternal();
private:
const BString _WebAppRepositoryCode() const;
private: private:
Model* fModel; Model* fModel;
PackageInfoRef fPackageInfo; BString fPackageName;
}; };
#endif // POPULATE_PKG_USER_RATINGS_FROM_SERVER_PROCESS__H #endif // POPULATE_PKG_USER_RATINGS_FROM_SERVER_PROCESS__H
@@ -1,5 +1,5 @@
/* /*
* Copyright 2017-2021, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2017-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#include "ServerIconExportUpdateProcess.h" #include "ServerIconExportUpdateProcess.h"
@@ -8,13 +8,14 @@
#include <time.h> #include <time.h>
#include <AutoDeleter.h> #include <AutoDeleter.h>
#include <AutoLocker.h> #include <BufferIO.h>
#include <Catalog.h> #include <Catalog.h>
#include <FileIO.h> #include <FileIO.h>
#include "DataIOUtils.h" #include "DataIOUtils.h"
#include "HaikuDepotConstants.h" #include "HaikuDepotConstants.h"
#include "Logger.h" #include "Logger.h"
#include "PackageIconTarRepository.h"
#include "ServerHelper.h" #include "ServerHelper.h"
#include "StandardMetaDataJsonEventListener.h" #include "StandardMetaDataJsonEventListener.h"
#include "StorageUtils.h" #include "StorageUtils.h"
@@ -55,6 +56,7 @@ InfoJsonExtractEntryListener::InfoJsonExtractEntryListener()
{ {
} }
InfoJsonExtractEntryListener::~InfoJsonExtractEntryListener() InfoJsonExtractEntryListener::~InfoJsonExtractEntryListener()
{ {
} }
@@ -68,14 +70,12 @@ InfoJsonExtractEntryListener::GetInfoJsonData()
status_t status_t
InfoJsonExtractEntryListener::Handle( const TarArchiveHeader& header, InfoJsonExtractEntryListener::Handle(const TarArchiveHeader& header, size_t offset, BDataIO* data)
size_t offset, BDataIO *data)
{ {
if (header.Length() > 0 && header.FileName() == ENTRY_PATH_METADATA) { if (header.Length() > 0 && header.FileName() == ENTRY_PATH_METADATA) {
status_t copyResult = DataIOUtils::CopyAll(&fInfoJsonData, data); status_t copyResult = DataIOUtils::CopyAll(&fInfoJsonData, data);
if (copyResult == B_OK) { if (copyResult == B_OK) {
HDINFO("[InfoJsonExtractEntryListener] did extract [%s]", HDINFO("[InfoJsonExtractEntryListener] did extract [%s]", ENTRY_PATH_METADATA);
ENTRY_PATH_METADATA);
fInfoJsonData.Seek(0, SEEK_SET); fInfoJsonData.Seek(0, SEEK_SET);
return B_CANCELED; return B_CANCELED;
// this will prevent further scanning of the tar file // this will prevent further scanning of the tar file
@@ -88,10 +88,8 @@ InfoJsonExtractEntryListener::Handle( const TarArchiveHeader& header,
/*! This constructor will locate the cached data in a standardized location /*! This constructor will locate the cached data in a standardized location
*/ */
ServerIconExportUpdateProcess::ServerIconExportUpdateProcess(Model* model,
ServerIconExportUpdateProcess::ServerIconExportUpdateProcess(
Model* model,
uint32 serverProcessOptions) uint32 serverProcessOptions)
: :
AbstractSingleFileServerProcess(serverProcessOptions), AbstractSingleFileServerProcess(serverProcessOptions),
@@ -122,8 +120,20 @@ ServerIconExportUpdateProcess::Description() const
status_t status_t
ServerIconExportUpdateProcess::ProcessLocalData() ServerIconExportUpdateProcess::ProcessLocalData()
{ {
status_t result = fModel->InitPackageIconRepository(); BPath tarPath;
_NotifyPackagesWithIconsInDepots(); status_t result = GetLocalPath(tarPath);
if (result == B_OK) {
PackageIconTarRepository* tarRepository = new PackageIconTarRepository(tarPath);
result = tarRepository->Init();
if (result == B_OK)
fModel->SetIconRepository(PackageIconRepositoryRef(tarRepository, true));
else
delete tarRepository;
}
return result; return result;
} }
@@ -144,7 +154,7 @@ ServerIconExportUpdateProcess::GetLocalPath(BPath& path) const
*/ */
status_t status_t
ServerIconExportUpdateProcess::IfModifiedSinceHeaderValue(BString& headerValue) const ServerIconExportUpdateProcess::IfModifiedSinceHeaderValue(BString& headerValue)
{ {
headerValue.SetTo(""); headerValue.SetTo("");
@@ -164,13 +174,13 @@ ServerIconExportUpdateProcess::IfModifiedSinceHeaderValue(BString& headerValue)
} }
if (result == B_OK) { if (result == B_OK) {
BFile *tarIo = new BFile(tarPath.Path(), O_RDONLY); BFile* tarIo = new BFile(tarPath.Path(), O_RDONLY);
ObjectDeleter<BFile> tarIoDeleter(tarIo); BBufferIO* bufferTarIo = new BBufferIO(tarIo, 10 * 1024, true);
InfoJsonExtractEntryListener* extractDataListener ObjectDeleter<BBufferIO> tarIoDeleter(bufferTarIo);
= new InfoJsonExtractEntryListener();
ObjectDeleter<InfoJsonExtractEntryListener> extractDataListenerDeleter( InfoJsonExtractEntryListener* extractDataListener = new InfoJsonExtractEntryListener();
extractDataListener); ObjectDeleter<InfoJsonExtractEntryListener> extractDataListenerDeleter(extractDataListener);
result = TarArchiveService::ForEachEntry(*tarIo, extractDataListener); result = TarArchiveService::ForEachEntry(*bufferTarIo, extractDataListener);
if (result == B_CANCELED) { if (result == B_CANCELED) {
// the cancellation is expected because it will cancel when it finds // the cancellation is expected because it will cancel when it finds
@@ -181,26 +191,20 @@ ServerIconExportUpdateProcess::IfModifiedSinceHeaderValue(BString& headerValue)
StandardMetaData metaData; StandardMetaData metaData;
BString metaDataJsonPath; BString metaDataJsonPath;
GetStandardMetaDataJsonPath(metaDataJsonPath); GetStandardMetaDataJsonPath(metaDataJsonPath);
StandardMetaDataJsonEventListener parseInfoJsonListener( StandardMetaDataJsonEventListener parseInfoJsonListener(metaDataJsonPath, metaData);
metaDataJsonPath, metaData); BPrivate::BJson::Parse(&(extractDataListener->GetInfoJsonData()),
BPrivate::BJson::Parse(
&(extractDataListener->GetInfoJsonData()),
&parseInfoJsonListener); &parseInfoJsonListener);
result = parseInfoJsonListener.ErrorStatus(); result = parseInfoJsonListener.ErrorStatus();
// An example of this output would be; 'Fri, 24 Oct 2014 19:32:27 +0000' // An example of this output would be; 'Fri, 24 Oct 2014 19:32:27 +0000'
if (result == B_OK) { if (result == B_OK)
SetIfModifiedSinceHeaderValueFromMetaData( SetIfModifiedSinceHeaderValueFromMetaData(headerValue, metaData);
headerValue, metaData); else
} else { HDERROR("[%s] unable to parse the meta data from the tar file", Name());
HDERROR("[%s] unable to parse the meta data from the tar file",
Name());
}
} else { } else {
HDERROR("[%s] did not find the metadata [%s] in the tar", HDERROR("[%s] did not find the metadata [%s] in the tar", Name(), ENTRY_PATH_METADATA);
Name(), ENTRY_PATH_METADATA);
result = B_BAD_DATA; result = B_BAD_DATA;
} }
} }
@@ -218,8 +222,7 @@ ServerIconExportUpdateProcess::GetStandardMetaDataPath(BPath& path) const
void void
ServerIconExportUpdateProcess::GetStandardMetaDataJsonPath( ServerIconExportUpdateProcess::GetStandardMetaDataJsonPath(BString& jsonPath) const
BString& jsonPath) const
{ {
jsonPath.SetTo("$"); jsonPath.SetTo("$");
// the "$" here indicates that the data is at the top level. // the "$" here indicates that the data is at the top level.
@@ -231,27 +234,3 @@ ServerIconExportUpdateProcess::UrlPathComponent()
{ {
return "/__pkgicon/all.tar.gz"; return "/__pkgicon/all.tar.gz";
} }
void
ServerIconExportUpdateProcess::_NotifyPackagesWithIconsInDepots() const
{
for (int32 d = 0; d < fModel->CountDepots(); d++) {
_NotifyPackagesWithIconsInDepot(fModel->DepotAtIndex(d));
}
}
void
ServerIconExportUpdateProcess::_NotifyPackagesWithIconsInDepot(
const DepotInfoRef& depot) const
{
PackageIconRepository& packageIconRepository
= fModel->GetPackageIconRepository();
for (int32 p = 0; p < depot->CountPackages(); p++) {
AutoLocker<BLocker> locker(fModel->Lock());
const PackageInfoRef& packageInfoRef = depot->PackageAtIndex(p);
if (packageIconRepository.HasAnyIcon(packageInfoRef->Name()))
packageInfoRef->NotifyChangedIcon();
}
}
@@ -1,5 +1,5 @@
/* /*
* Copyright 2017-2020, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2017-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef SERVER_ICON_EXPORT_UPDATE_PROCESS_H #ifndef SERVER_ICON_EXPORT_UPDATE_PROCESS_H
@@ -32,7 +32,7 @@ public:
virtual status_t GetLocalPath(BPath& path) const; virtual status_t GetLocalPath(BPath& path) const;
virtual status_t IfModifiedSinceHeaderValue( virtual status_t IfModifiedSinceHeaderValue(
BString& headerValue) const; BString& headerValue);
virtual status_t GetStandardMetaDataPath(BPath& path) const; virtual status_t GetStandardMetaDataPath(BPath& path) const;
@@ -42,11 +42,6 @@ public:
protected: protected:
virtual BString UrlPathComponent(); virtual BString UrlPathComponent();
private:
void _NotifyPackagesWithIconsInDepots() const;
void _NotifyPackagesWithIconsInDepot(
const DepotInfoRef& depotInfo) const;
private: private:
Model* fModel; Model* fModel;
@@ -1,5 +1,5 @@
/* /*
* Copyright 2017-2024, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2017-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
@@ -33,18 +33,22 @@
#define B_TRANSLATION_CONTEXT "ServerPkgDataUpdateProcess" #define B_TRANSLATION_CONTEXT "ServerPkgDataUpdateProcess"
/*! This package listener (not at the JSON level) is feeding in the static std::size_t kPackageBatchSize = 100;
packages as they are parsed and processing them.
static uint32 kPackageChangeMask = PKG_CHANGED_RATINGS | PKG_CHANGED_SCREENSHOTS
| PKG_CHANGED_CLASSIFICATION | PKG_CHANGED_LOCALIZED_TEXT | PKG_CHANGED_LOCAL_INFO
| PKG_CHANGED_CORE_INFO;
/*! This package listener (not at the JSON level) is feeding in the
packages as they are parsed and processing them.
*/ */
class PackageFillingPkgListener : public DumpExportPkgListener { class PackageFillingPkgListener : public DumpExportPkgListener {
public: public:
PackageFillingPkgListener(Model *model, PackageFillingPkgListener(Model *model, Stoppable* stoppable);
BString& depotName, Stoppable* stoppable);
virtual ~PackageFillingPkgListener(); virtual ~PackageFillingPkgListener();
virtual bool ConsumePackage(const PackageInfoRef& package,
DumpExportPkg* pkg);
virtual bool Handle(DumpExportPkg* item); virtual bool Handle(DumpExportPkg* item);
virtual void Complete(); virtual void Complete();
@@ -53,26 +57,36 @@ public:
private: private:
static ScreenshotInfoRef _CreateScreenshot(DumpExportPkgScreenshot* screenshot); static ScreenshotInfoRef _CreateScreenshot(DumpExportPkgScreenshot* screenshot);
void _FlushUpdatedPackages();
const PackageInfoRef
_PackageForName(const BString& name) const;
const PackageInfoRef
_CreateUpdatePackage(const PackageInfoRef& package,
const DumpExportPkg* pkg);
void _InitCategories();
private: private:
BString fDepotName;
Model* fModel; Model* fModel;
std::vector<CategoryRef> std::map<BString, CategoryRef>
fCategories; fCategories;
std::vector<PackageInfoRef>
fUpdatedPackages;
Stoppable* fStoppable; Stoppable* fStoppable;
uint32 fCount; uint32 fCount;
bool fDebugEnabled; bool fDebugEnabled;
}; };
PackageFillingPkgListener::PackageFillingPkgListener(Model* model, PackageFillingPkgListener::PackageFillingPkgListener(Model* model, Stoppable* stoppable)
BString& depotName, Stoppable* stoppable)
: :
fDepotName(depotName),
fModel(model), fModel(model),
fStoppable(stoppable), fStoppable(stoppable),
fCount(0), fCount(0),
fDebugEnabled(Logger::IsDebugEnabled()) fDebugEnabled(Logger::IsDebugEnabled())
{ {
_InitCategories();
} }
@@ -81,110 +95,133 @@ PackageFillingPkgListener::~PackageFillingPkgListener()
} }
bool void
PackageFillingPkgListener::ConsumePackage(const PackageInfoRef& package, PackageFillingPkgListener::_InitCategories()
DumpExportPkg* pkg) {
std::vector<CategoryRef> categories = fModel->Categories();
std::vector<CategoryRef>::const_iterator it;
for (it = categories.begin(); it != categories.end(); it++) {
const CategoryRef& category = *it;
fCategories[category->Code()] = category;
}
if (fCategories.empty())
HDERROR("there are no categories present");
}
const PackageInfoRef
PackageFillingPkgListener::_PackageForName(const BString& name) const
{
return fModel->PackageForName(name);
}
void
PackageFillingPkgListener::_FlushUpdatedPackages()
{
fModel->AddPackagesWithChange(fUpdatedPackages, kPackageChangeMask);
fUpdatedPackages.clear();
}
/*! This method will produce a new package with the data provided by the
server.
*/
const PackageInfoRef
PackageFillingPkgListener::_CreateUpdatePackage(const PackageInfoRef& package,
const DumpExportPkg* pkg)
{ {
int32 i; int32 i;
// Collects all of the changes here into one set of notifications to PackageClassificationInfoBuilder classificationInfoBuilder(
// the package's listeners. This way the quantity of BMessages package->PackageClassificationInfo());
// communicated back to listeners is considerably reduced. See stop PackageLocalizedTextBuilder localizedTextBuilder(package->LocalizedText());
// invocation later in this method. PackageLocalInfoBuilder localInfoBuilder(package->LocalInfo());
PackageCoreInfoBuilder coreInfoBuilder(package->CoreInfo());
PackageScreenshotInfoBuilder screenshotInfoBuilder;
// don't want to start with the existing data; just take what comes from the server.
PackageUserRatingInfoBuilder userRatingBuilder;
package->StartCollatingChanges(); localizedTextBuilder.WithHasChangelog(pkg->HasChangelog());
PackageClassificationInfoRef packageClassificationInfo(new PackageClassificationInfo(), true);
PackageLocalizedTextRef localizedText = PackageUtils::NewLocalizedText(package);
PackageLocalInfoRef localInfo = PackageUtils::NewLocalInfo(package);
PackageCoreInfoRef coreInfo = PackageUtils::NewCoreInfo(package);
localizedText->SetHasChangelog(pkg->HasChangelog());
if (0 != pkg->CountPkgVersions()) { if (0 != pkg->CountPkgVersions()) {
// this makes the assumption that the only version will be the // this makes the assumption that the only version will be the
// latest one. // latest one.
DumpExportPkgVersion* pkgVersion = pkg->PkgVersionsItemAt(0); DumpExportPkgVersion* pkgVersion = pkg->PkgVersionsItemAt(0);
if (!pkgVersion->TitleIsNull()) if (!pkgVersion->TitleIsNull())
localizedText->SetTitle(*(pkgVersion->Title())); localizedTextBuilder.WithTitle(*(pkgVersion->Title()));
if (!pkgVersion->SummaryIsNull()) if (!pkgVersion->SummaryIsNull())
localizedText->SetSummary(*(pkgVersion->Summary())); localizedTextBuilder.WithSummary(*(pkgVersion->Summary()));
if (!pkgVersion->DescriptionIsNull()) if (!pkgVersion->DescriptionIsNull())
localizedText->SetDescription(*(pkgVersion->Description())); localizedTextBuilder.WithDescription(*(pkgVersion->Description()));
if (!pkgVersion->PayloadLengthIsNull()) if (!pkgVersion->PayloadLengthIsNull())
localInfo->SetSize(static_cast<off_t>(pkgVersion->PayloadLength())); localInfoBuilder.WithSize(static_cast<off_t>(pkgVersion->PayloadLength()));
if (!pkgVersion->CreateTimestampIsNull()) { if (!pkgVersion->CreateTimestampIsNull()) {
PackageVersionRef version = coreInfo->Version(); PackageVersionRef versionRef = PackageUtils::Version(package);
if (!version.IsSet()) { if (versionRef.IsSet()) {
version = PackageVersionRef(new PackageVersion(), true); PackageVersion* version = versionRef.Get();
coreInfo->SetVersion(version); versionRef = PackageVersionRef(
new PackageVersion(*version, pkgVersion->CreateTimestamp()), true);
} else {
versionRef
= PackageVersionRef(new PackageVersion(pkgVersion->CreateTimestamp()), true);
} }
version->SetCreateTimestamp(pkgVersion->CreateTimestamp()); coreInfoBuilder.WithVersion(versionRef);
} }
} }
if (!pkg->DerivedRatingIsNull()) {
// TODO; unify the naming here!
userRatingBuilder.WithSummary(UserRatingSummaryBuilder()
.WithAverageRating(pkg->DerivedRating())
.WithRatingCount(pkg->DerivedRatingSampleSize())
.BuildRef());
}
int32 countPkgCategories = pkg->CountPkgCategories(); int32 countPkgCategories = pkg->CountPkgCategories();
for (i = 0; i < countPkgCategories; i++) { for (i = 0; i < countPkgCategories; i++) {
BString* categoryCode = pkg->PkgCategoriesItemAt(i)->Code(); BString* categoryCode = pkg->PkgCategoriesItemAt(i)->Code();
CategoryRef category = fModel->CategoryByCode(*categoryCode); CategoryRef category = fCategories[*categoryCode];
if (!category.IsSet()) { if (!category.IsSet())
HDERROR("unable to find the category for [%s]", HDERROR("unable to find the category for [%s]", categoryCode->String());
categoryCode->String()); else
} else classificationInfoBuilder.AddCategory(category);
packageClassificationInfo->AddCategory(category);
}
if (!pkg->DerivedRatingIsNull()) {
UserRatingInfoRef userRatingInfo(new UserRatingInfo(), true);
UserRatingSummaryRef userRatingSummary(new UserRatingSummary(), true);
// TODO; unify the naming here!
userRatingSummary->SetAverageRating(pkg->DerivedRating());
userRatingSummary->SetRatingCount(pkg->DerivedRatingSampleSize());
userRatingInfo->SetSummary(userRatingSummary);
package->SetUserRatingInfo(userRatingInfo);
} }
if (!pkg->ProminenceOrderingIsNull()) if (!pkg->ProminenceOrderingIsNull())
packageClassificationInfo->SetProminence(static_cast<uint32>(pkg->ProminenceOrdering())); classificationInfoBuilder.WithProminence(static_cast<uint32>(pkg->ProminenceOrdering()));
if (!pkg->IsNativeDesktopIsNull()) if (!pkg->IsNativeDesktopIsNull())
packageClassificationInfo->SetIsNativeDesktop(pkg->IsNativeDesktop()); classificationInfoBuilder.WithIsNativeDesktop(pkg->IsNativeDesktop());
int32 countPkgScreenshots = pkg->CountPkgScreenshots(); int32 countPkgScreenshots = pkg->CountPkgScreenshots();
PackageScreenshotInfoRef screenshotInfo(new PackageScreenshotInfo(), true);
for (i = 0; i < countPkgScreenshots; i++) { for (i = 0; i < countPkgScreenshots; i++) {
DumpExportPkgScreenshot* screenshot = pkg->PkgScreenshotsItemAt(i); DumpExportPkgScreenshot* screenshot = pkg->PkgScreenshotsItemAt(i);
screenshotInfo->AddScreenshot(_CreateScreenshot(screenshot)); screenshotInfoBuilder.AddScreenshot(_CreateScreenshot(screenshot));
} }
package->SetScreenshotInfo(screenshotInfo); return PackageInfoBuilder(package)
package->SetLocalizedText(localizedText); .WithScreenshotInfo(screenshotInfoBuilder.BuildRef())
package->SetLocalInfo(localInfo); .WithUserRatingInfo(userRatingBuilder.BuildRef())
package->SetCoreInfo(coreInfo); .WithLocalizedText(localizedTextBuilder.BuildRef())
.WithLocalInfo(localInfoBuilder.BuildRef())
HDTRACE("did populate data for [%s] (%s)", pkg->Name()->String(), .WithCoreInfo(coreInfoBuilder.BuildRef())
fDepotName.String()); .WithPackageClassificationInfo(classificationInfoBuilder.BuildRef())
.BuildRef();
fCount++;
package->SetPackageClassificationInfo(packageClassificationInfo);
package->EndCollatingChanges();
return !fStoppable->WasStopped();
} }
@@ -208,23 +245,20 @@ PackageFillingPkgListener::Count()
bool bool
PackageFillingPkgListener::Handle(DumpExportPkg* pkg) PackageFillingPkgListener::Handle(DumpExportPkg* pkg)
{ {
AutoLocker<BLocker> locker(fModel->Lock()); const BString packageName = *(pkg->Name());
DepotInfoRef depot = fModel->DepotForName(fDepotName); PackageInfoRef package = _PackageForName(packageName);
if (depot.Get() != NULL) { if (package.IsSet()) {
const BString packageName = *(pkg->Name()); fUpdatedPackages.push_back(_CreateUpdatePackage(package, pkg));
PackageInfoRef package = depot->PackageByName(packageName); HDTRACE("did populate data for [%s]", pkg->Name()->String());
if (package.Get() != NULL) fCount++;
ConsumePackage(package, pkg);
else {
HDINFO("[PackageFillingPkgListener] unable to find the pkg [%s]",
packageName.String());
}
} else { } else {
HDINFO("[PackageFillingPkgListener] unable to find the depot [%s]", HDINFO("[PackageFillingPkgListener] unable to find the pkg [%s]", packageName.String());
fDepotName.String());
} }
if (fUpdatedPackages.size() > kPackageBatchSize)
_FlushUpdatedPackages();
return !fStoppable->WasStopped(); return !fStoppable->WasStopped();
} }
@@ -232,12 +266,11 @@ PackageFillingPkgListener::Handle(DumpExportPkg* pkg)
void void
PackageFillingPkgListener::Complete() PackageFillingPkgListener::Complete()
{ {
_FlushUpdatedPackages();
} }
ServerPkgDataUpdateProcess::ServerPkgDataUpdateProcess( ServerPkgDataUpdateProcess::ServerPkgDataUpdateProcess(BString depotName, Model* model,
BString depotName,
Model *model,
uint32 serverProcessOptions) uint32 serverProcessOptions)
: :
AbstractSingleFileServerProcess(serverProcessOptions), AbstractSingleFileServerProcess(serverProcessOptions),
@@ -245,9 +278,7 @@ ServerPkgDataUpdateProcess::ServerPkgDataUpdateProcess(
fDepotName(depotName) fDepotName(depotName)
{ {
fName.SetToFormat("ServerPkgDataUpdateProcess<%s>", depotName.String()); fName.SetToFormat("ServerPkgDataUpdateProcess<%s>", depotName.String());
fDescription.SetTo( fDescription.SetTo(B_TRANSLATE("Synchronizing package data for repository '%REPO_NAME%'"));
B_TRANSLATE("Synchronizing package data for repository "
"'%REPO_NAME%'"));
fDescription.ReplaceAll("%REPO_NAME%", depotName.String()); fDescription.ReplaceAll("%REPO_NAME%", depotName.String());
} }
@@ -287,7 +318,6 @@ ServerPkgDataUpdateProcess::GetLocalPath(BPath& path) const
BString webAppRepositorySourceCode = _DeriveWebAppRepositorySourceCode(); BString webAppRepositorySourceCode = _DeriveWebAppRepositorySourceCode();
if (!webAppRepositorySourceCode.IsEmpty()) { if (!webAppRepositorySourceCode.IsEmpty()) {
AutoLocker<BLocker> locker(fModel->Lock());
return StorageUtils::DumpExportPkgDataPath(path, webAppRepositorySourceCode, return StorageUtils::DumpExportPkgDataPath(path, webAppRepositorySourceCode,
fModel->PreferredLanguage()); fModel->PreferredLanguage());
} }
@@ -301,15 +331,12 @@ ServerPkgDataUpdateProcess::ProcessLocalData()
{ {
BStopWatch watch("ServerPkgDataUpdateProcess::ProcessLocalData", true); BStopWatch watch("ServerPkgDataUpdateProcess::ProcessLocalData", true);
PackageFillingPkgListener* itemListener = PackageFillingPkgListener* itemListener = new PackageFillingPkgListener(fModel, this);
new PackageFillingPkgListener(fModel, fDepotName, this); ObjectDeleter<PackageFillingPkgListener> itemListenerDeleter(itemListener);
ObjectDeleter<PackageFillingPkgListener>
itemListenerDeleter(itemListener);
BulkContainerDumpExportPkgJsonListener* listener = BulkContainerDumpExportPkgJsonListener* listener
new BulkContainerDumpExportPkgJsonListener(itemListener); = new BulkContainerDumpExportPkgJsonListener(itemListener);
ObjectDeleter<BulkContainerDumpExportPkgJsonListener> ObjectDeleter<BulkContainerDumpExportPkgJsonListener> listenerDeleter(listener);
listenerDeleter(listener);
BPath localPath; BPath localPath;
status_t result = GetLocalPath(localPath); status_t result = GetLocalPath(localPath);
@@ -324,8 +351,8 @@ ServerPkgDataUpdateProcess::ProcessLocalData()
if (Logger::IsInfoEnabled()) { if (Logger::IsInfoEnabled()) {
double secs = watch.ElapsedTime() / 1000000.0; double secs = watch.ElapsedTime() / 1000000.0;
HDINFO("[%s] did process %" B_PRIi32 " packages' data " HDINFO("[%s] did process %" B_PRIi32 " packages' data in (%6.3g secs)", Name(),
"in (%6.3g secs)", Name(), itemListener->Count(), secs); itemListener->Count(), secs);
} }
return listener->ErrorStatus(); return listener->ErrorStatus();
@@ -340,8 +367,7 @@ ServerPkgDataUpdateProcess::GetStandardMetaDataPath(BPath& path) const
void void
ServerPkgDataUpdateProcess::GetStandardMetaDataJsonPath( ServerPkgDataUpdateProcess::GetStandardMetaDataJsonPath(BString& jsonPath) const
BString& jsonPath) const
{ {
jsonPath.SetTo("$.info"); jsonPath.SetTo("$.info");
} }
@@ -352,9 +378,8 @@ ServerPkgDataUpdateProcess::_DeriveWebAppRepositorySourceCode() const
{ {
const DepotInfo* depot = fModel->DepotForName(fDepotName); const DepotInfo* depot = fModel->DepotForName(fDepotName);
if (depot == NULL) { if (depot == NULL)
return BString(); return BString();
}
return depot->WebAppRepositorySourceCode(); return depot->WebAppRepositorySourceCode();
} }
@@ -364,8 +389,8 @@ status_t
ServerPkgDataUpdateProcess::RunInternal() ServerPkgDataUpdateProcess::RunInternal()
{ {
if (_DeriveWebAppRepositorySourceCode().IsEmpty()) { if (_DeriveWebAppRepositorySourceCode().IsEmpty()) {
HDINFO("[%s] am not updating data for depot [%s] as there is no" HDINFO("[%s] am not updating data for depot [%s] as there is no web app repository source "
" web app repository source code available", "code available",
Name(), fDepotName.String()); Name(), fDepotName.String());
return B_OK; return B_OK;
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2020, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2019-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
@@ -32,8 +32,7 @@
#define B_TRANSLATION_CONTEXT "ServerReferenceDataUpdateProcess" #define B_TRANSLATION_CONTEXT "ServerReferenceDataUpdateProcess"
ServerReferenceDataUpdateProcess::ServerReferenceDataUpdateProcess( ServerReferenceDataUpdateProcess::ServerReferenceDataUpdateProcess(Model* model,
Model* model,
uint32 serverProcessOptions) uint32 serverProcessOptions)
: :
AbstractSingleFileServerProcess(serverProcessOptions), AbstractSingleFileServerProcess(serverProcessOptions),
@@ -65,7 +64,6 @@ BString
ServerReferenceDataUpdateProcess::UrlPathComponent() ServerReferenceDataUpdateProcess::UrlPathComponent()
{ {
BString result; BString result;
AutoLocker<BLocker> locker(fModel->Lock());
result.SetToFormat("/__reference/all-%s.json.gz", fModel->PreferredLanguage()->ID()); result.SetToFormat("/__reference/all-%s.json.gz", fModel->PreferredLanguage()->ID());
return result; return result;
} }
@@ -74,7 +72,6 @@ ServerReferenceDataUpdateProcess::UrlPathComponent()
status_t status_t
ServerReferenceDataUpdateProcess::GetLocalPath(BPath& path) const ServerReferenceDataUpdateProcess::GetLocalPath(BPath& path) const
{ {
AutoLocker<BLocker> locker(fModel->Lock());
return StorageUtils::DumpExportReferenceDataPath(path, fModel->PreferredLanguage()); return StorageUtils::DumpExportReferenceDataPath(path, fModel->PreferredLanguage());
} }
@@ -82,8 +79,7 @@ ServerReferenceDataUpdateProcess::GetLocalPath(BPath& path) const
status_t status_t
ServerReferenceDataUpdateProcess::ProcessLocalData() ServerReferenceDataUpdateProcess::ProcessLocalData()
{ {
SingleDumpExportReferenceJsonListener* listener = SingleDumpExportReferenceJsonListener* listener = new SingleDumpExportReferenceJsonListener();
new SingleDumpExportReferenceJsonListener();
BPath localPath; BPath localPath;
status_t result = GetLocalPath(localPath); status_t result = GetLocalPath(localPath);
@@ -120,91 +116,68 @@ ServerReferenceDataUpdateProcess::_ProcessData(DumpExportReference* data)
status_t status_t
ServerReferenceDataUpdateProcess::_ProcessNaturalLanguages( ServerReferenceDataUpdateProcess::_ProcessNaturalLanguages(DumpExportReference* data)
DumpExportReference* data)
{ {
HDINFO("[%s] will populate from %" B_PRId32 " possible natural languages", HDINFO("[%s] will populate from %" B_PRId32 " possible natural languages", Name(),
Name(), data->CountNaturalLanguages()); data->CountNaturalLanguages());
AutoLocker<BLocker> locker(fModel->Lock());
LanguageRepository* languageRepository = fModel->Languages(); std::vector<LanguageRef> languages;
languageRepository->Clear();
int32 count = 0; int32 count = 0;
for (int32 i = 0; i < data->CountNaturalLanguages(); i++) { for (int32 i = data->CountNaturalLanguages() - 1; i > 0; i--) {
DumpExportReferenceNaturalLanguage* naturalLanguage = DumpExportReferenceNaturalLanguage* naturalLanguage = data->NaturalLanguagesItemAt(i);
data->NaturalLanguagesItemAt(i); Language* language = new Language(*(naturalLanguage->Code()), *(naturalLanguage->Name()),
languageRepository->AddLanguage( naturalLanguage->IsPopular());
LanguageRef(new Language(*(naturalLanguage->Code()), *(naturalLanguage->Name()), languages.push_back(LanguageRef(language, true));
naturalLanguage->IsPopular()),
true));
count++; count++;
} }
// it could be that the preferred language does not exist in the if (!languages.empty()) {
// list. In this case it is necessary to choose one from the list. fModel->SetLanguagesAndPreferred(languages, LocaleUtils::DeriveDefaultLanguage(languages));
fModel->SetPreferredLanguage(LocaleUtils::DeriveDefaultLanguage(fModel->Languages())); HDINFO("[%s] did add %" B_PRId32 " supported languages", Name(), count);
}
HDINFO("[%s] did add %" B_PRId32 " supported languages", Name(), count);
return B_OK; return B_OK;
} }
status_t status_t
ServerReferenceDataUpdateProcess::_ProcessPkgCategories( ServerReferenceDataUpdateProcess::_ProcessPkgCategories(DumpExportReference* data)
DumpExportReference* data)
{ {
HDINFO("[%s] will populate %" B_PRId32 " pkg categories", HDINFO("[%s] will populate %" B_PRId32 " pkg categories", Name(), data->CountPkgCategories());
Name(), data->CountPkgCategories());
std::vector<CategoryRef> assembledCategories; std::vector<CategoryRef> assembledCategories;
for (int32 i = 0; i < data->CountPkgCategories(); i++) { for (int32 i = 0; i < data->CountPkgCategories(); i++) {
DumpExportReferencePkgCategory* pkgCategory = DumpExportReferencePkgCategory* pkgCategory = data->PkgCategoriesItemAt(i);
data->PkgCategoriesItemAt(i); assembledCategories.push_back(
assembledCategories.push_back(CategoryRef( CategoryRef(new PackageCategory(*(pkgCategory->Code()), *(pkgCategory->Name())), true));
new PackageCategory(
*(pkgCategory->Code()),
*(pkgCategory->Name())
),
true));
} }
{ fModel->SetCategories(assembledCategories);
AutoLocker<BLocker> locker(fModel->Lock());
fModel->AddCategories(assembledCategories);
}
return B_OK; return B_OK;
} }
status_t status_t
ServerReferenceDataUpdateProcess::_ProcessRatingStabilities( ServerReferenceDataUpdateProcess::_ProcessRatingStabilities(DumpExportReference* data)
DumpExportReference* data)
{ {
HDINFO("[%s] will populate %" B_PRId32 " rating stabilities", HDINFO("[%s] will populate %" B_PRId32 " rating stabilities", Name(),
Name(), data->CountUserRatingStabilities()); data->CountUserRatingStabilities());
std::vector<RatingStabilityRef> assembledRatingStabilities; std::vector<RatingStabilityRef> assembledRatingStabilities;
for (int32 i = 0; i < data->CountUserRatingStabilities(); i++) { for (int32 i = 0; i < data->CountUserRatingStabilities(); i++) {
DumpExportReferenceUserRatingStability* ratingStability = DumpExportReferenceUserRatingStability* ratingStability
data->UserRatingStabilitiesItemAt(i); = data->UserRatingStabilitiesItemAt(i);
assembledRatingStabilities.push_back(RatingStabilityRef( assembledRatingStabilities.push_back(
new RatingStability( RatingStabilityRef(new RatingStability(*(ratingStability->Code()),
*(ratingStability->Code()), *(ratingStability->Name()), ratingStability->Ordering()),
*(ratingStability->Name()), true));
ratingStability->Ordering()
),
true));
} }
{ fModel->SetRatingStabilities(assembledRatingStabilities);
AutoLocker<BLocker> locker(fModel->Lock());
fModel->AddRatingStabilities(assembledRatingStabilities);
}
return B_OK; return B_OK;
} }
@@ -218,8 +191,7 @@ ServerReferenceDataUpdateProcess::GetStandardMetaDataPath(BPath& path) const
void void
ServerReferenceDataUpdateProcess::GetStandardMetaDataJsonPath( ServerReferenceDataUpdateProcess::GetStandardMetaDataJsonPath(BString& jsonPath) const
BString& jsonPath) const
{ {
jsonPath.SetTo("$.info"); jsonPath.SetTo("$.info");
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright 2017-2023, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2017-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#include "ServerRepositoryDataUpdateProcess.h" #include "ServerRepositoryDataUpdateProcess.h"
@@ -17,10 +17,10 @@
#include "DumpExportRepository.h" #include "DumpExportRepository.h"
#include "DumpExportRepositoryJsonListener.h" #include "DumpExportRepositoryJsonListener.h"
#include "DumpExportRepositorySource.h" #include "DumpExportRepositorySource.h"
#include "Logger.h"
#include "PackageInfo.h" #include "PackageInfo.h"
#include "ServerSettings.h" #include "ServerSettings.h"
#include "StorageUtils.h" #include "StorageUtils.h"
#include "Logger.h"
#undef B_TRANSLATION_CONTEXT #undef B_TRANSLATION_CONTEXT
@@ -28,13 +28,12 @@
/*! This repository listener (not at the JSON level) is feeding in the /*! This repository listener (not at the JSON level) is feeding in the
repositories as they are parsed and processing them. Processing repositories as they are parsed and processing them. Processing
includes finding the matching depot record and coupling the data includes finding the matching depot record and coupling the data
from the server with the data about the depot. from the server with the data about the depot.
*/ */
class DepotMatchingRepositoryListener : class DepotMatchingRepositoryListener : public DumpExportRepositoryListener {
public DumpExportRepositoryListener {
public: public:
DepotMatchingRepositoryListener(Model* model, DepotMatchingRepositoryListener(Model* model,
Stoppable* stoppable); Stoppable* stoppable);
@@ -60,14 +59,16 @@ private:
private: private:
Model* fModel; Model* fModel;
Stoppable* fStoppable; Stoppable* fStoppable;
std::vector<DepotInfoRef>
fDepots;
}; };
DepotMatchingRepositoryListener::DepotMatchingRepositoryListener( DepotMatchingRepositoryListener::DepotMatchingRepositoryListener(Model* model, Stoppable* stoppable)
Model* model, Stoppable* stoppable)
: :
fModel(model), fModel(model),
fStoppable(stoppable) fStoppable(stoppable),
fDepots()
{ {
} }
@@ -79,43 +80,41 @@ DepotMatchingRepositoryListener::~DepotMatchingRepositoryListener()
void void
DepotMatchingRepositoryListener::_SetupRepositoryData(DepotInfoRef& depot, DepotMatchingRepositoryListener::_SetupRepositoryData(DepotInfoRef& depot,
DumpExportRepository* repository, DumpExportRepository* repository, DumpExportRepositorySource* repositorySource)
DumpExportRepositorySource* repositorySource)
{ {
BString* repositoryCode = repository->Code(); BString* repositoryCode = repository->Code();
BString* repositorySourceCode = repositorySource->Code(); BString* repositorySourceCode = repositorySource->Code();
depot->SetWebAppRepositoryCode(*repositoryCode); DepotInfoBuilder depotBuilder(depot);
depot->SetWebAppRepositorySourceCode(*repositorySourceCode);
depotBuilder.WithWebAppRepositoryCode(*repositoryCode);
depotBuilder.WithWebAppRepositorySourceCode(*repositorySourceCode);
DepotInfoRef depotInfo = depotBuilder.BuildRef();
fDepots.push_back(depotInfo);
if (Logger::IsDebugEnabled()) { if (Logger::IsDebugEnabled()) {
HDDEBUG("[DepotMatchingRepositoryListener] associated depot [%s] (%s) " HDDEBUG("[DepotMatchingRepositoryListener] associated depot [%s] (%s) "
"with server repository source [%s] (%s)", "with server repository source [%s] (%s)",
depot->Name().String(), depot->Name().String(), depot->Identifier().String(), repositorySourceCode->String(),
depot->Identifier().String(),
repositorySourceCode->String(),
repositorySource->Identifier()->String()); repositorySource->Identifier()->String());
} else { } else {
HDINFO("[DepotMatchingRepositoryListener] associated depot [%s] with " HDINFO("[DepotMatchingRepositoryListener] associated depot [%s] with "
"server repository source [%s]", "server repository source [%s]",
depot->Name().String(), depot->Name().String(), repositorySourceCode->String());
repositorySourceCode->String());
} }
} }
void void
DepotMatchingRepositoryListener::Handle(const BString& identifier, DepotMatchingRepositoryListener::Handle(const BString& identifier, DumpExportRepository* repository,
DumpExportRepository* repository,
DumpExportRepositorySource* repositorySource) DumpExportRepositorySource* repositorySource)
{ {
if (!identifier.IsEmpty()) { if (!identifier.IsEmpty()) {
AutoLocker<BLocker> locker(fModel->Lock()); DepotInfoRef depotForIdentifier = fModel->DepotForIdentifier(identifier);
for (int32 i = 0; i < fModel->CountDepots(); i++) {
DepotInfoRef depot = fModel->DepotAtIndex(i); if (depotForIdentifier.IsSet())
if (identifier == depot->Identifier()) _SetupRepositoryData(depotForIdentifier, repository, repositorySource);
_SetupRepositoryData(depot, repository, repositorySource);
}
} }
} }
@@ -130,13 +129,8 @@ DepotMatchingRepositoryListener::Handle(DumpExportRepository* repository,
// there may be additional identifiers for the remote repository and // there may be additional identifiers for the remote repository and
// these should also be taken into consideration. // these should also be taken into consideration.
for(int32 i = 0; for (int32 i = 0; i < repositorySource->CountExtraIdentifiers(); i++)
i < repositorySource->CountExtraIdentifiers(); Handle(*(repositorySource->ExtraIdentifiersItemAt(i)), repository, repositorySource);
i++)
{
Handle(*(repositorySource->ExtraIdentifiersItemAt(i)), repository,
repositorySource);
}
} }
@@ -153,11 +147,11 @@ DepotMatchingRepositoryListener::Handle(DumpExportRepository* repository)
void void
DepotMatchingRepositoryListener::Complete() DepotMatchingRepositoryListener::Complete()
{ {
fModel->SetDepots(fDepots);
} }
ServerRepositoryDataUpdateProcess::ServerRepositoryDataUpdateProcess( ServerRepositoryDataUpdateProcess::ServerRepositoryDataUpdateProcess(Model* model,
Model* model,
uint32 serverProcessOptions) uint32 serverProcessOptions)
: :
AbstractSingleFileServerProcess(serverProcessOptions), AbstractSingleFileServerProcess(serverProcessOptions),
@@ -189,7 +183,6 @@ BString
ServerRepositoryDataUpdateProcess::UrlPathComponent() ServerRepositoryDataUpdateProcess::UrlPathComponent()
{ {
BString result; BString result;
AutoLocker<BLocker> locker(fModel->Lock());
result.SetToFormat("/__repository/all-%s.json.gz", fModel->PreferredLanguage()->ID()); result.SetToFormat("/__repository/all-%s.json.gz", fModel->PreferredLanguage()->ID());
return result; return result;
} }
@@ -198,7 +191,6 @@ ServerRepositoryDataUpdateProcess::UrlPathComponent()
status_t status_t
ServerRepositoryDataUpdateProcess::GetLocalPath(BPath& path) const ServerRepositoryDataUpdateProcess::GetLocalPath(BPath& path) const
{ {
AutoLocker<BLocker> locker(fModel->Lock());
return StorageUtils::DumpExportRepositoryDataPath(path, fModel->PreferredLanguage()); return StorageUtils::DumpExportRepositoryDataPath(path, fModel->PreferredLanguage());
} }
@@ -206,15 +198,13 @@ ServerRepositoryDataUpdateProcess::GetLocalPath(BPath& path) const
status_t status_t
ServerRepositoryDataUpdateProcess::ProcessLocalData() ServerRepositoryDataUpdateProcess::ProcessLocalData()
{ {
DepotMatchingRepositoryListener* itemListener = DepotMatchingRepositoryListener* itemListener
new DepotMatchingRepositoryListener(fModel, this); = new DepotMatchingRepositoryListener(fModel, this);
ObjectDeleter<DepotMatchingRepositoryListener> ObjectDeleter<DepotMatchingRepositoryListener> itemListenerDeleter(itemListener);
itemListenerDeleter(itemListener);
BulkContainerDumpExportRepositoryJsonListener* listener = BulkContainerDumpExportRepositoryJsonListener* listener
new BulkContainerDumpExportRepositoryJsonListener(itemListener); = new BulkContainerDumpExportRepositoryJsonListener(itemListener);
ObjectDeleter<BulkContainerDumpExportRepositoryJsonListener> ObjectDeleter<BulkContainerDumpExportRepositoryJsonListener> listenerDeleter(listener);
listenerDeleter(listener);
BPath localPath; BPath localPath;
status_t result = GetLocalPath(localPath); status_t result = GetLocalPath(localPath);
@@ -239,8 +229,7 @@ ServerRepositoryDataUpdateProcess::GetStandardMetaDataPath(BPath& path) const
void void
ServerRepositoryDataUpdateProcess::GetStandardMetaDataJsonPath( ServerRepositoryDataUpdateProcess::GetStandardMetaDataJsonPath(BString& jsonPath) const
BString& jsonPath) const
{ {
jsonPath.SetTo("$.info"); jsonPath.SetTo("$.info");
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright 2020, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2020-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
@@ -63,7 +63,7 @@ UserDetailVerifierProcess::RunInternal()
case B_OK: case B_OK:
if (!userDetail.Agreement().IsLatest()) { if (!userDetail.Agreement().IsLatest()) {
HDINFO("the user has not agreed to the latest user usage" HDINFO("the user has not agreed to the latest user usage"
" conditions."); " conditions.");
fListener->UserUsageConditionsNotLatest(userDetail); fListener->UserUsageConditionsNotLatest(userDetail);
} }
break; break;
@@ -84,12 +84,9 @@ UserDetailVerifierProcess::_ShouldVerify()
return false; return false;
} }
{ if (fModel->Nickname().IsEmpty()) {
AutoLocker<BLocker> locker(fModel->Lock()); HDINFO("no nickname --> will not verify user");
if (fModel->Nickname().IsEmpty()) { return false;
HDINFO("no nickname --> will not verify user");
return false;
}
} }
return true; return true;
@@ -99,16 +96,14 @@ UserDetailVerifierProcess::_ShouldVerify()
status_t status_t
UserDetailVerifierProcess::_TryFetchUserDetail(UserDetail& userDetail) UserDetailVerifierProcess::_TryFetchUserDetail(UserDetail& userDetail)
{ {
WebAppInterface* interface = fModel->GetWebAppInterface(); WebAppInterfaceRef interface = fModel->WebApp();
BMessage userDetailResponse; BMessage userDetailResponse;
status_t result; status_t result;
result = interface->RetrieveCurrentUserDetail(userDetailResponse); result = interface->RetrieveCurrentUserDetail(userDetailResponse);
if (result != B_OK) { if (result != B_OK)
HDERROR("a problem has arisen retrieving the current user detail: %s", HDERROR("a problem has arisen retrieving the current user detail: %s", strerror(result));
strerror(result));
}
if (result == B_OK) { if (result == B_OK) {
int32 errorCode = WebAppInterface::ErrorCodeFromResponse(userDetailResponse); int32 errorCode = WebAppInterface::ErrorCodeFromResponse(userDetailResponse);
@@ -120,7 +115,7 @@ UserDetailVerifierProcess::_TryFetchUserDetail(UserDetail& userDetail)
break; break;
default: default:
HDERROR("a problem has arisen retrieving the current user " HDERROR("a problem has arisen retrieving the current user "
"detail for user [%s]: jrpc error code %" B_PRId32 "", "detail for user [%s]: jrpc error code %" B_PRId32 "",
fModel->Nickname().String(), errorCode); fModel->Nickname().String(), errorCode);
result = B_ERROR; result = B_ERROR;
break; break;
@@ -139,4 +134,4 @@ UserDetailVerifierProcess::_TryFetchUserDetail(UserDetail& userDetail)
} }
return result; return result;
} }
+136 -196
View File
@@ -1,6 +1,6 @@
/* /*
* Copyright 2014, Stephan Aßmus <superstippi@gmx.de>. * Copyright 2014, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2016-2024, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2016-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#include "WebAppInterface.h" #include "WebAppInterface.h"
@@ -14,8 +14,8 @@
#include <HttpHeaders.h> #include <HttpHeaders.h>
#include <HttpRequest.h> #include <HttpRequest.h>
#include <Json.h> #include <Json.h>
#include <JsonTextWriter.h>
#include <JsonMessageWriter.h> #include <JsonMessageWriter.h>
#include <JsonTextWriter.h>
#include <UrlContext.h> #include <UrlContext.h>
#include <UrlProtocolListener.h> #include <UrlProtocolListener.h>
#include <UrlProtocolRoster.h> #include <UrlProtocolRoster.h>
@@ -89,12 +89,10 @@ public:
static BHttpRequest* static BHttpRequest*
make_http_request(const BUrl& url, BDataIO* output, make_http_request(const BUrl& url, BDataIO* output, BUrlProtocolListener* listener = NULL,
BUrlProtocolListener* listener = NULL,
BUrlContext* context = NULL) BUrlContext* context = NULL)
{ {
BUrlRequest* request = BUrlProtocolRoster::MakeRequest(url, output, BUrlRequest* request = BUrlProtocolRoster::MakeRequest(url, output, listener, context);
listener, context);
BHttpRequest* httpRequest = dynamic_cast<BHttpRequest*>(request); BHttpRequest* httpRequest = dynamic_cast<BHttpRequest*>(request);
if (httpRequest == NULL) { if (httpRequest == NULL) {
delete request; delete request;
@@ -109,8 +107,9 @@ enum {
}; };
WebAppInterface::WebAppInterface() WebAppInterface::WebAppInterface(const UserCredentials& value)
{ {
_SetCredentials(value);
} }
@@ -120,9 +119,8 @@ WebAppInterface::~WebAppInterface()
void void
WebAppInterface::SetCredentials(const UserCredentials& value) WebAppInterface::_SetCredentials(const UserCredentials& value)
{ {
AutoLocker<BLocker> lock(&fLock);
if (fCredentials != value) { if (fCredentials != value) {
fCredentials = value; fCredentials = value;
fAccessToken.Clear(); fAccessToken.Clear();
@@ -133,7 +131,6 @@ WebAppInterface::SetCredentials(const UserCredentials& value)
const BString& const BString&
WebAppInterface::Nickname() WebAppInterface::Nickname()
{ {
AutoLocker<BLocker> lock(&fLock);
return fCredentials.Nickname(); return fCredentials.Nickname();
} }
@@ -212,21 +209,18 @@ WebAppInterface::RetrieveUserRatingsForPackageForDisplay(
requestEnvelopeWriter.WriteObjectEnd(); requestEnvelopeWriter.WriteObjectEnd();
return _SendJsonRequest("user-rating/search-user-ratings", return _SendJsonRequest("user-rating/search-user-ratings", requestEnvelopeData,
requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData), _LengthAndSeekToZero(requestEnvelopeData), 0, message);
0, message);
} }
status_t status_t
WebAppInterface::RetrieveUserRatingForPackageAndVersionByUser( WebAppInterface::RetrieveUserRatingForPackageAndVersionByUser(const BString& packageName,
const BString& packageName, const BPackageVersion& version, const BPackageVersion& version, const BString& architecture,
const BString& architecture, const BString& webAppRepositoryCode, const BString& webAppRepositorySourceCode,
const BString& webAppRepositoryCode,
const BString& webAppRepositorySourceCode,
const BString& userNickname, BMessage& message) const BString& userNickname, BMessage& message)
{ {
// BHttpRequest later takes ownership of this. // BHttpRequest later takes ownership of this.
BMallocIO* requestEnvelopeData = new BMallocIO(); BMallocIO* requestEnvelopeData = new BMallocIO();
BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData); BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
@@ -270,10 +264,8 @@ WebAppInterface::RetrieveUserRatingForPackageAndVersionByUser(
requestEnvelopeWriter.WriteObjectEnd(); requestEnvelopeWriter.WriteObjectEnd();
return _SendJsonRequest( return _SendJsonRequest("user-rating/get-user-rating-by-user-and-pkg-version",
"user-rating/get-user-rating-by-user-and-pkg-version", requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData), NEEDS_AUTHORIZATION,
requestEnvelopeData,
_LengthAndSeekToZero(requestEnvelopeData), NEEDS_AUTHORIZATION,
message); message);
} }
@@ -285,12 +277,12 @@ WebAppInterface::RetrieveUserRatingForPackageAndVersionByUser(
*/ */
status_t status_t
WebAppInterface::RetrieveUserDetailForCredentials( WebAppInterface::RetrieveUserDetailForCredentials(const UserCredentials& credentials,
const UserCredentials& credentials, BMessage& message) BMessage& message)
{ {
if (!credentials.IsValid()) { if (!credentials.IsValid()) {
debugger("the credentials supplied are invalid so it is not possible " debugger("the credentials supplied are invalid so it is not possible "
"to obtain the user detail"); "to obtain the user detail");
} }
status_t result = B_OK; status_t result = B_OK;
@@ -301,9 +293,7 @@ WebAppInterface::RetrieveUserDetailForCredentials(
BMessage authenticateResponseEnvelopeMessage; BMessage authenticateResponseEnvelopeMessage;
if (result == B_OK) { if (result == B_OK) {
result = AuthenticateUser( result = AuthenticateUser(credentials.Nickname(), credentials.PasswordClear(),
credentials.Nickname(),
credentials.PasswordClear(),
authenticateResponseEnvelopeMessage); authenticateResponseEnvelopeMessage);
} }
@@ -313,7 +303,7 @@ WebAppInterface::RetrieveUserDetailForCredentials(
result = UnpackAccessToken(authenticateResponseEnvelopeMessage, accessToken); result = UnpackAccessToken(authenticateResponseEnvelopeMessage, accessToken);
if (result == B_OK) { if (result == B_OK) {
// BHttpRequest later takes ownership of this. // BHttpRequest later takes ownership of this.
BMallocIO* requestEnvelopeData = new BMallocIO(); BMallocIO* requestEnvelopeData = new BMallocIO();
BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData); BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
@@ -322,9 +312,8 @@ WebAppInterface::RetrieveUserDetailForCredentials(
requestEnvelopeWriter.WriteString(credentials.Nickname().String()); requestEnvelopeWriter.WriteString(credentials.Nickname().String());
requestEnvelopeWriter.WriteObjectEnd(); requestEnvelopeWriter.WriteObjectEnd();
result = _SendJsonRequest("user/get-user", accessToken, result = _SendJsonRequest("user/get-user", accessToken, requestEnvelopeData,
requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData), _LengthAndSeekToZero(requestEnvelopeData), NEEDS_AUTHORIZATION, message);
NEEDS_AUTHORIZATION, message);
// note that the credentials used here are passed in as args. // note that the credentials used here are passed in as args.
} }
@@ -350,12 +339,10 @@ WebAppInterface::RetrieveCurrentUserDetail(BMessage& message)
*/ */
/*static*/ status_t /*static*/ status_t
WebAppInterface::UnpackUserDetail(BMessage& responseEnvelopeMessage, WebAppInterface::UnpackUserDetail(BMessage& responseEnvelopeMessage, UserDetail& userDetail)
UserDetail& userDetail)
{ {
BMessage resultMessage; BMessage resultMessage;
status_t result = responseEnvelopeMessage.FindMessage( status_t result = responseEnvelopeMessage.FindMessage("result", &resultMessage);
"result", &resultMessage);
if (result != B_OK) { if (result != B_OK) {
HDERROR("bad response envelope missing 'result' entry"); HDERROR("bad response envelope missing 'result' entry");
@@ -367,29 +354,24 @@ WebAppInterface::UnpackUserDetail(BMessage& responseEnvelopeMessage,
userDetail.SetNickname(nickname); userDetail.SetNickname(nickname);
BMessage agreementMessage; BMessage agreementMessage;
if (resultMessage.FindMessage("userUsageConditionsAgreement", if (resultMessage.FindMessage("userUsageConditionsAgreement", &agreementMessage) == B_OK) {
&agreementMessage) == B_OK) {
BString code; BString code;
BDateTime agreedToTimestamp; BDateTime agreedToTimestamp;
BString userUsageConditionsCode; BString userUsageConditionsCode;
UserUsageConditionsAgreement agreement = userDetail.Agreement(); UserUsageConditionsAgreement agreement = userDetail.Agreement();
bool isLatest; bool isLatest;
if (agreementMessage.FindString("userUsageConditionsCode", if (agreementMessage.FindString("userUsageConditionsCode", &userUsageConditionsCode)
&userUsageConditionsCode) == B_OK) { == B_OK) {
agreement.SetCode(userUsageConditionsCode); agreement.SetCode(userUsageConditionsCode);
} }
double timestampAgreedMillis; double timestampAgreedMillis;
if (agreementMessage.FindDouble("timestampAgreed", if (agreementMessage.FindDouble("timestampAgreed", &timestampAgreedMillis) == B_OK)
&timestampAgreedMillis) == B_OK) { agreement.SetTimestampAgreed((uint64)timestampAgreedMillis);
agreement.SetTimestampAgreed((uint64) timestampAgreedMillis);
}
if (agreementMessage.FindBool("isLatest", &isLatest) if (agreementMessage.FindBool("isLatest", &isLatest) == B_OK)
== B_OK) {
agreement.SetIsLatest(isLatest); agreement.SetIsLatest(isLatest);
}
userDetail.SetAgreement(agreement); userDetail.SetAgreement(agreement);
} }
@@ -399,20 +381,18 @@ WebAppInterface::UnpackUserDetail(BMessage& responseEnvelopeMessage,
/*! When an authentication API call is made, the response (if successful) will /*! When an authentication API call is made, the response (if successful) will
return an access token in the response. This method will take the response return an access token in the response. This method will take the response
from the server and will parse out the access token data into the supplied from the server and will parse out the access token data into the supplied
object. object.
*/ */
/*static*/ status_t /*static*/ status_t
WebAppInterface::UnpackAccessToken(BMessage& responseEnvelopeMessage, WebAppInterface::UnpackAccessToken(BMessage& responseEnvelopeMessage, AccessToken& accessToken)
AccessToken& accessToken)
{ {
status_t result; status_t result;
BMessage resultMessage; BMessage resultMessage;
result = responseEnvelopeMessage.FindMessage( result = responseEnvelopeMessage.FindMessage("result", &resultMessage);
"result", &resultMessage);
if (result != B_OK) { if (result != B_OK) {
HDERROR("bad response envelope missing 'result' entry"); HDERROR("bad response envelope missing 'result' entry");
@@ -469,12 +449,10 @@ WebAppInterface::UnpackAccessToken(BMessage& responseEnvelopeMessage,
*/ */
status_t status_t
WebAppInterface::RetrieveUserUsageConditions(const BString& code, WebAppInterface::RetrieveUserUsageConditions(const BString& code, UserUsageConditions& conditions)
UserUsageConditions& conditions)
{ {
BMessage responseEnvelopeMessage; BMessage responseEnvelopeMessage;
status_t result = _RetrieveUserUsageConditionsMeta(code, status_t result = _RetrieveUserUsageConditionsMeta(code, responseEnvelopeMessage);
responseEnvelopeMessage);
if (result != B_OK) if (result != B_OK)
return result; return result;
@@ -489,11 +467,10 @@ WebAppInterface::RetrieveUserUsageConditions(const BString& code,
double metaDataMinimumAge; double metaDataMinimumAge;
BString copyMarkdown; BString copyMarkdown;
if ( (resultMessage.FindString("code", &metaDataCode) != B_OK) if ((resultMessage.FindString("code", &metaDataCode) != B_OK)
|| (resultMessage.FindDouble( || (resultMessage.FindDouble("minimumAge", &metaDataMinimumAge) != B_OK)) {
"minimumAge", &metaDataMinimumAge) != B_OK) ) {
HDERROR("unexpected response from server with missing user usage " HDERROR("unexpected response from server with missing user usage "
"conditions data"); "conditions data");
return B_BAD_DATA; return B_BAD_DATA;
} }
@@ -505,17 +482,15 @@ WebAppInterface::RetrieveUserUsageConditions(const BString& code,
conditions.SetCode(metaDataCode); conditions.SetCode(metaDataCode);
conditions.SetMinimumAge(metaDataMinimumAge); conditions.SetMinimumAge(metaDataMinimumAge);
conditions.SetCopyMarkdown( conditions.SetCopyMarkdown(BString(static_cast<const char*>(copyMarkdownData->Buffer()),
BString(static_cast<const char*>(copyMarkdownData->Buffer()), copyMarkdownData->BufferLength()));
copyMarkdownData->BufferLength()));
return B_OK; return B_OK;
} }
status_t status_t
WebAppInterface::AgreeUserUsageConditions(const BString& code, WebAppInterface::AgreeUserUsageConditions(const BString& code, BMessage& responsePayload)
BMessage& responsePayload)
{ {
BMallocIO* requestEnvelopeData = new BMallocIO(); BMallocIO* requestEnvelopeData = new BMallocIO();
BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData); BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
@@ -529,15 +504,13 @@ WebAppInterface::AgreeUserUsageConditions(const BString& code,
// now fetch this information into an object. // now fetch this information into an object.
return _SendJsonRequest("user/agree-user-usage-conditions", return _SendJsonRequest("user/agree-user-usage-conditions", requestEnvelopeData,
requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData), _LengthAndSeekToZero(requestEnvelopeData), NEEDS_AUTHORIZATION, responsePayload);
NEEDS_AUTHORIZATION, responsePayload);
} }
status_t status_t
WebAppInterface::_RetrieveUserUsageConditionsMeta(const BString& code, WebAppInterface::_RetrieveUserUsageConditionsMeta(const BString& code, BMessage& message)
BMessage& message)
{ {
BMallocIO* requestEnvelopeData = new BMallocIO(); BMallocIO* requestEnvelopeData = new BMallocIO();
BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData); BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
@@ -553,33 +526,26 @@ WebAppInterface::_RetrieveUserUsageConditionsMeta(const BString& code,
// now fetch this information into an object. // now fetch this information into an object.
return _SendJsonRequest("user/get-user-usage-conditions", return _SendJsonRequest("user/get-user-usage-conditions", requestEnvelopeData,
requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData), _LengthAndSeekToZero(requestEnvelopeData), 0, message);
0, message);
} }
status_t status_t
WebAppInterface::_RetrieveUserUsageConditionsCopy(const BString& code, WebAppInterface::_RetrieveUserUsageConditionsCopy(const BString& code, BDataIO* stream)
BDataIO* stream)
{ {
return _SendRawGetRequest( return _SendRawGetRequest(BString("/__user/usageconditions/") << code << "/document.md",
BString("/__user/usageconditions/") << code << "/document.md",
stream); stream);
} }
status_t status_t
WebAppInterface::CreateUserRating(const BString& packageName, WebAppInterface::CreateUserRating(const BString& packageName, const BPackageVersion& version,
const BPackageVersion& version, const BString& architecture, const BString& webAppRepositoryCode,
const BString& architecture, const BString& webAppRepositorySourceCode, const BString& naturalLanguageCode,
const BString& webAppRepositoryCode, // This is the "ID" in the ICU system; the term `code` is used with the
const BString& webAppRepositorySourceCode, // server system.
const BString& naturalLanguageCode, const BString& comment, const BString& stability, int rating, BMessage& message)
// This is the "ID" in the ICU system; the term `code` is used with the
// server system.
const BString& comment,
const BString& stability, int rating, BMessage& message)
{ {
BMallocIO* requestEnvelopeData = new BMallocIO(); BMallocIO* requestEnvelopeData = new BMallocIO();
// BHttpRequest later takes ownership of this. // BHttpRequest later takes ownership of this.
@@ -628,7 +594,7 @@ WebAppInterface::CreateUserRating(const BString& packageName,
if (rating > 0.0f) { if (rating > 0.0f) {
requestEnvelopeWriter.WriteObjectName("rating"); requestEnvelopeWriter.WriteObjectName("rating");
requestEnvelopeWriter.WriteInteger(rating); requestEnvelopeWriter.WriteInteger(rating);
} }
if (stability.Length() > 0) { if (stability.Length() > 0) {
@@ -643,19 +609,16 @@ WebAppInterface::CreateUserRating(const BString& packageName,
requestEnvelopeWriter.WriteObjectEnd(); requestEnvelopeWriter.WriteObjectEnd();
return _SendJsonRequest("user-rating/create-user-rating", return _SendJsonRequest("user-rating/create-user-rating", requestEnvelopeData,
requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData), _LengthAndSeekToZero(requestEnvelopeData), NEEDS_AUTHORIZATION, message);
NEEDS_AUTHORIZATION, message);
} }
status_t status_t
WebAppInterface::UpdateUserRating(const BString& ratingID, WebAppInterface::UpdateUserRating(const BString& ratingID, const BString& naturalLanguageCode,
const BString& naturalLanguageCode, // This is the "ID" in the ICU system; the term `code` is used with the
// This is the "ID" in the ICU system; the term `code` is used with the // server system.
// server system. const BString& comment, const BString& stability, int rating, bool active, BMessage& message)
const BString& comment,
const BString& stability, int rating, bool active, BMessage& message)
{ {
BMallocIO* requestEnvelopeData = new BMallocIO(); BMallocIO* requestEnvelopeData = new BMallocIO();
// BHttpRequest later takes ownership of this. // BHttpRequest later takes ownership of this.
@@ -696,23 +659,21 @@ WebAppInterface::UpdateUserRating(const BString& ratingID,
requestEnvelopeWriter.WriteObjectEnd(); requestEnvelopeWriter.WriteObjectEnd();
return _SendJsonRequest("user-rating/update-user-rating", return _SendJsonRequest("user-rating/update-user-rating", requestEnvelopeData,
requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData), _LengthAndSeekToZero(requestEnvelopeData), NEEDS_AUTHORIZATION, message);
NEEDS_AUTHORIZATION, message);
} }
/*! This method will call to the server to get a screenshot that will fit into /*! This method will call to the server to get a screenshot that will fit into
the specified width and height. the specified width and height.
*/ */
status_t status_t
WebAppInterface::RetrieveScreenshot(const BString& code, WebAppInterface::RetrieveScreenshot(const BString& code, int32 width, int32 height, BDataIO* stream)
int32 width, int32 height, BDataIO* stream)
{ {
return _SendRawGetRequest( return _SendRawGetRequest(BString("/__pkgscreenshot/")
BString("/__pkgscreenshot/") << code << ".png" << "?tw=" << code << ".png" << "?tw=" << width << "&th=" << height,
<< width << "&th=" << height, stream); stream);
} }
@@ -726,25 +687,20 @@ WebAppInterface::RequestCaptcha(BMessage& message)
requestEnvelopeWriter.WriteObjectStart(); requestEnvelopeWriter.WriteObjectStart();
requestEnvelopeWriter.WriteObjectEnd(); requestEnvelopeWriter.WriteObjectEnd();
return _SendJsonRequest("captcha/generate-captcha", return _SendJsonRequest("captcha/generate-captcha", requestEnvelopeData,
requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData), _LengthAndSeekToZero(requestEnvelopeData), 0, message);
0, message);
} }
status_t status_t
WebAppInterface::CreateUser(const BString& nickName, WebAppInterface::CreateUser(const BString& nickName, const BString& passwordClear,
const BString& passwordClear, const BString& email, const BString& captchaToken, const BString& captchaResponse,
const BString& email,
const BString& captchaToken,
const BString& captchaResponse,
const BString& naturalLanguageCode, const BString& naturalLanguageCode,
// This is the "ID" in the ICU system; the term `code` is used with the // This is the "ID" in the ICU system; the term `code` is used with the
// server system. // server system.
const BString& userUsageConditionsCode, const BString& userUsageConditionsCode, BMessage& message)
BMessage& message)
{ {
// BHttpRequest later takes ownership of this. // BHttpRequest later takes ownership of this.
BMallocIO* requestEnvelopeData = new BMallocIO(); BMallocIO* requestEnvelopeData = new BMallocIO();
BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData); BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
@@ -776,7 +732,7 @@ WebAppInterface::CreateUser(const BString& nickName,
/*! This method will authenticate the user set in the credentials and will /*! This method will authenticate the user set in the credentials and will
retain the resultant access token for authenticating any latter API calls. retain the resultant access token for authenticating any latter API calls.
*/ */
status_t status_t
@@ -796,7 +752,8 @@ WebAppInterface::AuthenticateUserRetainingAccessToken()
status_t status_t
WebAppInterface::_AuthenticateUserRetainingAccessToken(const BString& nickName, WebAppInterface::_AuthenticateUserRetainingAccessToken(const BString& nickName,
const BString& passwordClear) { const BString& passwordClear)
{
AutoLocker<BLocker> lock(&fLock); AutoLocker<BLocker> lock(&fLock);
fAccessToken.Clear(); fAccessToken.Clear();
@@ -817,8 +774,8 @@ WebAppInterface::_AuthenticateUserRetainingAccessToken(const BString& nickName,
status_t status_t
WebAppInterface::AuthenticateUser(const BString& nickName, WebAppInterface::AuthenticateUser(const BString& nickName, const BString& passwordClear,
const BString& passwordClear, BMessage& message) BMessage& message)
{ {
BMallocIO* requestEnvelopeData = new BMallocIO(); BMallocIO* requestEnvelopeData = new BMallocIO();
// BHttpRequest later takes ownership of this. // BHttpRequest later takes ownership of this.
@@ -833,9 +790,8 @@ WebAppInterface::AuthenticateUser(const BString& nickName,
requestEnvelopeWriter.WriteObjectEnd(); requestEnvelopeWriter.WriteObjectEnd();
return _SendJsonRequest("user/authenticate-user", return _SendJsonRequest("user/authenticate-user", requestEnvelopeData,
requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData), _LengthAndSeekToZero(requestEnvelopeData), 0, message);
0, message);
} }
@@ -893,19 +849,16 @@ WebAppInterface::IncrementViewCounter(const PackageInfoRef package, const DepotI
requestEnvelopeWriter.WriteObjectEnd(); requestEnvelopeWriter.WriteObjectEnd();
return _SendJsonRequest("pkg/increment-view-counter", return _SendJsonRequest("pkg/increment-view-counter", requestEnvelopeData,
requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData), _LengthAndSeekToZero(requestEnvelopeData), 0, message);
0, message);
} }
status_t status_t
WebAppInterface::RetrievePasswordRequirements( WebAppInterface::RetrievePasswordRequirements(PasswordRequirements& passwordRequirements)
PasswordRequirements& passwordRequirements)
{ {
BMessage responseEnvelopeMessage; BMessage responseEnvelopeMessage;
status_t result = _RetrievePasswordRequirementsMeta( status_t result = _RetrievePasswordRequirementsMeta(responseEnvelopeMessage);
responseEnvelopeMessage);
if (result != B_OK) if (result != B_OK)
return result; return result;
@@ -922,13 +875,13 @@ WebAppInterface::RetrievePasswordRequirements(
double value; double value;
if (resultMessage.FindDouble("minPasswordLength", &value) == B_OK) if (resultMessage.FindDouble("minPasswordLength", &value) == B_OK)
passwordRequirements.SetMinPasswordLength((uint32) value); passwordRequirements.SetMinPasswordLength((uint32)value);
if (resultMessage.FindDouble("minPasswordUppercaseChar", &value) == B_OK) if (resultMessage.FindDouble("minPasswordUppercaseChar", &value) == B_OK)
passwordRequirements.SetMinPasswordUppercaseChar((uint32) value); passwordRequirements.SetMinPasswordUppercaseChar((uint32)value);
if (resultMessage.FindDouble("minPasswordDigitsChar", &value) == B_OK) if (resultMessage.FindDouble("minPasswordDigitsChar", &value) == B_OK)
passwordRequirements.SetMinPasswordDigitsChar((uint32) value); passwordRequirements.SetMinPasswordDigitsChar((uint32)value);
return result; return result;
} }
@@ -944,9 +897,8 @@ WebAppInterface::_RetrievePasswordRequirementsMeta(BMessage& message)
requestEnvelopeWriter.WriteObjectStart(); requestEnvelopeWriter.WriteObjectStart();
requestEnvelopeWriter.WriteObjectEnd(); requestEnvelopeWriter.WriteObjectEnd();
return _SendJsonRequest("user/get-password-requirements", return _SendJsonRequest("user/get-password-requirements", requestEnvelopeData,
requestEnvelopeData, _LengthAndSeekToZero(requestEnvelopeData), _LengthAndSeekToZero(requestEnvelopeData), 0, message);
0, message);
} }
@@ -966,7 +918,7 @@ WebAppInterface::ErrorCodeFromResponse(BMessage& responseEnvelopeMessage)
if (responseEnvelopeMessage.FindMessage("error", &error) == B_OK if (responseEnvelopeMessage.FindMessage("error", &error) == B_OK
&& error.FindDouble("code", &code) == B_OK) { && error.FindDouble("code", &code) == B_OK) {
return (int32) code; return (int32)code;
} }
return 0; return 0;
@@ -977,9 +929,8 @@ WebAppInterface::ErrorCodeFromResponse(BMessage& responseEnvelopeMessage)
status_t status_t
WebAppInterface::_SendJsonRequest(const char* urlPathComponents, WebAppInterface::_SendJsonRequest(const char* urlPathComponents, BPositionIO* requestData,
BPositionIO* requestData, size_t requestDataSize, uint32 flags, size_t requestDataSize, uint32 flags, BMessage& reply)
BMessage& reply)
{ {
bool needsAuthorization = (flags & NEEDS_AUTHORIZATION) != 0; bool needsAuthorization = (flags & NEEDS_AUTHORIZATION) != 0;
AccessToken accessToken; AccessToken accessToken;
@@ -987,15 +938,14 @@ WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
if (needsAuthorization) if (needsAuthorization)
accessToken = _ObtainValidAccessToken(); accessToken = _ObtainValidAccessToken();
return _SendJsonRequest(urlPathComponents, accessToken, requestData, return _SendJsonRequest(urlPathComponents, accessToken, requestData, requestDataSize, flags,
requestDataSize, flags, reply); reply);
} }
/*static*/ status_t /*static*/ status_t
WebAppInterface::_SendJsonRequest(const char* urlPathComponents, WebAppInterface::_SendJsonRequest(const char* urlPathComponents, const AccessToken& accessToken,
const AccessToken& accessToken, BPositionIO* requestData, BPositionIO* requestData, size_t requestDataSize, uint32 flags, BMessage& reply)
size_t requestDataSize, uint32 flags, BMessage& reply)
{ {
if (requestDataSize == 0) { if (requestDataSize == 0) {
HDINFO("%s; empty request payload", PROTOCOL_NAME); HDINFO("%s; empty request payload", PROTOCOL_NAME);
@@ -1004,14 +954,15 @@ WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
if (!ServerHelper::IsNetworkAvailable()) { if (!ServerHelper::IsNetworkAvailable()) {
HDDEBUG("%s; dropping request to ...[%s] as network is not" HDDEBUG("%s; dropping request to ...[%s] as network is not"
" available", PROTOCOL_NAME, urlPathComponents); " available",
PROTOCOL_NAME, urlPathComponents);
delete requestData; delete requestData;
return HD_NETWORK_INACCESSIBLE; return HD_NETWORK_INACCESSIBLE;
} }
if (ServerSettings::IsClientTooOld()) { if (ServerSettings::IsClientTooOld()) {
HDDEBUG("%s; dropping request to ...[%s] as client is too old", HDDEBUG("%s; dropping request to ...[%s] as client is too old", PROTOCOL_NAME,
PROTOCOL_NAME, urlPathComponents); urlPathComponents);
delete requestData; delete requestData;
return HD_CLIENT_TOO_OLD; return HD_CLIENT_TOO_OLD;
} }
@@ -1019,16 +970,14 @@ WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
bool needsAuthorization = (flags & NEEDS_AUTHORIZATION) != 0; bool needsAuthorization = (flags & NEEDS_AUTHORIZATION) != 0;
if (needsAuthorization && !accessToken.IsValid()) { if (needsAuthorization && !accessToken.IsValid()) {
HDDEBUG("%s; dropping request to ...[%s] as access token is not valid", HDDEBUG("%s; dropping request to ...[%s] as access token is not valid", PROTOCOL_NAME,
PROTOCOL_NAME, urlPathComponents); urlPathComponents);
delete requestData; delete requestData;
return B_NOT_ALLOWED; return B_NOT_ALLOWED;
} }
BUrl url = ServerSettings::CreateFullUrl(BString("/__api/v2/") BUrl url = ServerSettings::CreateFullUrl(BString("/__api/v2/") << urlPathComponents);
<< urlPathComponents); HDDEBUG("%s; will make request to [%s]", PROTOCOL_NAME, url.UrlString().String());
HDDEBUG("%s; will make request to [%s]", PROTOCOL_NAME,
url.UrlString().String());
// If the request payload is logged then it must be copied to local memory // If the request payload is logged then it must be copied to local memory
// from the stream. This then requires that the request data is then // from the stream. This then requires that the request data is then
@@ -1071,13 +1020,12 @@ WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
thread_id thread = request->Run(); thread_id thread = request->Run();
wait_for_thread(thread, NULL); wait_for_thread(thread, NULL);
const BHttpResult& result = dynamic_cast<const BHttpResult&>( const BHttpResult& result = dynamic_cast<const BHttpResult&>(request->Result());
request->Result());
int32 statusCode = result.StatusCode(); int32 statusCode = result.StatusCode();
HDDEBUG("%s; did receive http-status [%" B_PRId32 "] from [%s]", HDDEBUG("%s; did receive http-status [%" B_PRId32 "] from [%s]", PROTOCOL_NAME, statusCode,
PROTOCOL_NAME, statusCode, url.UrlString().String()); url.UrlString().String());
switch (statusCode) { switch (statusCode) {
case B_HTTP_STATUS_OK: case B_HTTP_STATUS_OK:
@@ -1089,8 +1037,8 @@ WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
default: default:
HDERROR("%s; request to endpoint [.../%s] failed with http " HDERROR("%s; request to endpoint [.../%s] failed with http "
"status [%" B_PRId32 "]\n", PROTOCOL_NAME, urlPathComponents, "status [%" B_PRId32 "]\n",
statusCode); PROTOCOL_NAME, urlPathComponents, statusCode);
return B_ERROR; return B_ERROR;
} }
@@ -1108,7 +1056,7 @@ WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
status_t status = jsonMessageWriter.ErrorStatus(); status_t status = jsonMessageWriter.ErrorStatus();
if (Logger::IsTraceEnabled() && status == B_BAD_DATA) { if (Logger::IsTraceEnabled() && status == B_BAD_DATA) {
BString resultString(static_cast<const char *>(replyData.Buffer()), BString resultString(static_cast<const char*>(replyData.Buffer()),
replyData.BufferLength()); replyData.BufferLength());
HDERROR("Parser choked on JSON:\n%s", resultString.String()); HDERROR("Parser choked on JSON:\n%s", resultString.String());
} }
@@ -1117,33 +1065,29 @@ WebAppInterface::_SendJsonRequest(const char* urlPathComponents,
status_t status_t
WebAppInterface::_SendJsonRequest(const char* urlPathComponents, WebAppInterface::_SendJsonRequest(const char* urlPathComponents, const BString& jsonString,
const BString& jsonString, uint32 flags, BMessage& reply) uint32 flags, BMessage& reply)
{ {
// gets 'adopted' by the subsequent http request. // gets 'adopted' by the subsequent http request.
BMemoryIO* data = new BMemoryIO(jsonString.String(), BMemoryIO* data = new BMemoryIO(jsonString.String(), jsonString.Length() - 1);
jsonString.Length() - 1);
return _SendJsonRequest(urlPathComponents, data, jsonString.Length() - 1, return _SendJsonRequest(urlPathComponents, data, jsonString.Length() - 1, flags, reply);
flags, reply);
} }
status_t status_t
WebAppInterface::_SendRawGetRequest(const BString urlPathComponents, WebAppInterface::_SendRawGetRequest(const BString urlPathComponents, BDataIO* stream)
BDataIO* stream)
{ {
BUrl url = ServerSettings::CreateFullUrl(urlPathComponents); BUrl url = ServerSettings::CreateFullUrl(urlPathComponents);
HDDEBUG("http-get; will make request to [%s]", HDDEBUG("http-get; will make request to [%s]", url.UrlString().String());
url.UrlString().String());
ProtocolListener listener; ProtocolListener listener;
BHttpHeaders headers; BHttpHeaders headers;
ServerSettings::AugmentHeaders(headers); ServerSettings::AugmentHeaders(headers);
BHttpRequest *request = make_http_request(url, stream, &listener); BHttpRequest* request = make_http_request(url, stream, &listener);
ObjectDeleter<BHttpRequest> _(request); ObjectDeleter<BHttpRequest> _(request);
if (request == NULL) if (request == NULL)
return B_ERROR; return B_ERROR;
@@ -1153,19 +1097,17 @@ WebAppInterface::_SendRawGetRequest(const BString urlPathComponents,
thread_id thread = request->Run(); thread_id thread = request->Run();
wait_for_thread(thread, NULL); wait_for_thread(thread, NULL);
const BHttpResult& result = dynamic_cast<const BHttpResult&>( const BHttpResult& result = dynamic_cast<const BHttpResult&>(request->Result());
request->Result());
int32 statusCode = result.StatusCode(); int32 statusCode = result.StatusCode();
HDDEBUG("http-get; did receive http-status [%" B_PRId32 "] from [%s]", HDDEBUG("http-get; did receive http-status [%" B_PRId32 "] from [%s]", statusCode,
statusCode, url.UrlString().String()); url.UrlString().String());
if (statusCode == 200) if (statusCode == 200)
return B_OK; return B_OK;
HDERROR("failed to get data from '%s': %" B_PRIi32 "", HDERROR("failed to get data from '%s': %" B_PRIi32 "", url.UrlString().String(), statusCode);
url.UrlString().String(), statusCode);
return B_ERROR; return B_ERROR;
} }
@@ -1183,17 +1125,16 @@ WebAppInterface::_LogPayload(BPositionIO* requestData, size_t size)
printf("%s; error logging payload", PROTOCOL_NAME); printf("%s; error logging payload", PROTOCOL_NAME);
} else { } else {
for (uint32 i = 0; i < size; i++) { for (uint32 i = 0; i < size; i++) {
bool esc = buffer[i] > 126 || bool esc = buffer[i] > 126 || (buffer[i] < 0x20 && buffer[i] != 0x0a);
(buffer[i] < 0x20 && buffer[i] != 0x0a);
if (esc) if (esc)
printf("\\u%02x", buffer[i]); printf("\\u%02x", buffer[i]);
else else
putchar(buffer[i]); putchar(buffer[i]);
} }
if (size == LOG_PAYLOAD_LIMIT) if (size == LOG_PAYLOAD_LIMIT)
printf("...(continues)"); printf("...(continues)");
} }
requestData->Seek(requestDataOffset, SEEK_SET); requestData->Seek(requestDataOffset, SEEK_SET);
@@ -1209,15 +1150,14 @@ off_t
WebAppInterface::_LengthAndSeekToZero(BPositionIO* data) WebAppInterface::_LengthAndSeekToZero(BPositionIO* data)
{ {
off_t dataSize = data->Position(); off_t dataSize = data->Position();
data->Seek(0, SEEK_SET); data->Seek(0, SEEK_SET);
return dataSize; return dataSize;
} }
UserCredentials UserCredentials
WebAppInterface::_Credentials() WebAppInterface::_Credentials()
{ {
AutoLocker<BLocker> lock(&fLock);
return fCredentials; return fCredentials;
} }
+9 -4
View File
@@ -1,6 +1,6 @@
/* /*
* Copyright 2014, Stephan Aßmus <superstippi@gmx.de>. * Copyright 2014, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2016-2024, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2016-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef WEB_APP_INTERFACE_H #ifndef WEB_APP_INTERFACE_H
@@ -10,6 +10,7 @@
#include <Application.h> #include <Application.h>
#include <JsonWriter.h> #include <JsonWriter.h>
#include <Locker.h> #include <Locker.h>
#include <Referenceable.h>
#include <String.h> #include <String.h>
#include <package/PackageVersion.h> #include <package/PackageVersion.h>
@@ -43,12 +44,12 @@ using BPackageKit::BPackageVersion;
#define RATING_NONE -1 #define RATING_NONE -1
class WebAppInterface { class WebAppInterface : public BReferenceable
{
public: public:
WebAppInterface(); WebAppInterface(const UserCredentials& value);
virtual ~WebAppInterface(); virtual ~WebAppInterface();
void SetCredentials(const UserCredentials& value);
const BString& Nickname(); const BString& Nickname();
status_t GetChangelog( status_t GetChangelog(
@@ -157,6 +158,7 @@ public:
BMessage& responseEnvelopeMessage, BMessage& responseEnvelopeMessage,
AccessToken& accessToken); AccessToken& accessToken);
private: private:
void _SetCredentials(const UserCredentials& value);
UserCredentials _Credentials(); UserCredentials _Credentials();
AccessToken _ObtainValidAccessToken(); AccessToken _ObtainValidAccessToken();
@@ -199,4 +201,7 @@ private:
}; };
typedef BReference<WebAppInterface> WebAppInterfaceRef;
#endif // WEB_APP_INTERFACE_H #endif // WEB_APP_INTERFACE_H
+25 -34
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 2017-2020, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2017-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#include "TarArchiveService.h" #include "TarArchiveService.h"
@@ -30,7 +30,7 @@
data for the parsed entry. data for the parsed entry.
*/ */
/*static*/ status_t /*static*/ status_t
TarArchiveService::GetEntry(BPositionIO& tarIo, TarArchiveHeader& header) TarArchiveService::GetEntry(BPositionIO& tarIo, TarArchiveHeader& header)
{ {
uint8 buffer[LENGTH_BLOCK]; uint8 buffer[LENGTH_BLOCK];
@@ -46,7 +46,7 @@ TarArchiveService::GetEntry(BPositionIO& tarIo, TarArchiveHeader& header)
useful for obtaining a catalog of items in the tar file for example. useful for obtaining a catalog of items in the tar file for example.
*/ */
/*status*/ status_t /*status*/ status_t
TarArchiveService::ForEachEntry(BPositionIO& tarIo, TarEntryListener* listener) TarArchiveService::ForEachEntry(BPositionIO& tarIo, TarEntryListener* listener)
{ {
uint8 buffer[LENGTH_BLOCK]; uint8 buffer[LENGTH_BLOCK];
@@ -58,21 +58,17 @@ TarArchiveService::ForEachEntry(BPositionIO& tarIo, TarEntryListener* listener)
memset(zero_buffer, 0, sizeof zero_buffer); memset(zero_buffer, 0, sizeof zero_buffer);
tarIo.Seek(offset, SEEK_SET); tarIo.Seek(offset, SEEK_SET);
while (result == B_OK while (result == B_OK && B_OK == (result = tarIo.ReadExactly(buffer, LENGTH_BLOCK))) {
&& B_OK == (result = tarIo.ReadExactly(
buffer, LENGTH_BLOCK))) {
if (memcmp(zero_buffer, buffer, sizeof zero_buffer) == 0) { if (memcmp(zero_buffer, buffer, sizeof zero_buffer) == 0) {
HDDEBUG("detected end of tar-ball after %" B_PRIu32 " items", HDTRACE("detected end of tar-ball after %" B_PRIu32 " items", countItemsRead);
countItemsRead);
return B_OK; // end of tar-ball. return B_OK; // end of tar-ball.
} }
TarArchiveHeader header; TarArchiveHeader header;
result = _ReadHeader(buffer, header); result = _ReadHeader(buffer, header);
HDTRACE("did read tar entry header for [%s]", HDTRACE("did read tar entry header for [%s]", header.FileName().String());
header.FileName().String());
if (result == B_OK) { if (result == B_OK) {
countItemsRead++; countItemsRead++;
@@ -81,8 +77,7 @@ TarArchiveService::ForEachEntry(BPositionIO& tarIo, TarEntryListener* listener)
// and/or just process the header information. // and/or just process the header information.
if (listener != NULL) { if (listener != NULL) {
BDataIO *entryData = new ConstraintedDataIO(&tarIo, BDataIO* entryData = new ConstraintedDataIO(&tarIo, header.Length());
header.Length());
ObjectDeleter<BDataIO> entryDataDeleter(entryData); ObjectDeleter<BDataIO> entryDataDeleter(entryData);
result = listener->Handle(header, offset, entryData); result = listener->Handle(header, offset, entryData);
} }
@@ -105,7 +100,8 @@ TarArchiveService::ForEachEntry(BPositionIO& tarIo, TarEntryListener* listener)
tar_file_type tar_file_type
TarArchiveService::_ReadHeaderFileType(unsigned char data) { TarArchiveService::_ReadHeaderFileType(unsigned char data)
{
switch (data) { switch (data) {
case 0: case 0:
case '0': case '0':
@@ -117,22 +113,20 @@ TarArchiveService::_ReadHeaderFileType(unsigned char data) {
/*static*/ int32 /*static*/ int32
TarArchiveService::_ReadHeaderStringLength(const uint8* data, TarArchiveService::_ReadHeaderStringLength(const uint8* data, size_t maxStringLength)
size_t maxStringLength)
{ {
int32 actualLength = 0; size_t actualLength = 0;
while (actualLength < (int32) maxStringLength && data[actualLength] != 0)
while (actualLength < maxStringLength && data[actualLength] != 0)
actualLength++; actualLength++;
return actualLength; return static_cast<int32>(actualLength);
} }
void void
TarArchiveService::_ReadHeaderString(const uint8 *data, size_t maxStringLength, TarArchiveService::_ReadHeaderString(const uint8* data, size_t maxStringLength, BString& result)
BString& result)
{ {
result.SetTo((const char *) data, result.SetTo((const char*)data, _ReadHeaderStringLength(data, maxStringLength));
_ReadHeaderStringLength(data, maxStringLength));
} }
@@ -140,7 +134,8 @@ TarArchiveService::_ReadHeaderString(const uint8 *data, size_t maxStringLength,
character in an number expressed in octal. character in an number expressed in octal.
*/ */
static bool tar_is_octal_digit(unsigned char c) static bool
tar_is_octal_digit(unsigned char c)
{ {
switch (c) { switch (c) {
case '0': case '0':
@@ -159,7 +154,7 @@ static bool tar_is_octal_digit(unsigned char c)
/*static*/ uint32 /*static*/ uint32
TarArchiveService::_ReadHeaderNumeric(const uint8 *data, size_t dataLength) TarArchiveService::_ReadHeaderNumeric(const uint8* data, size_t dataLength)
{ {
uint32 actualLength = 0; uint32 actualLength = 0;
@@ -187,7 +182,7 @@ TarArchiveService::_CalculateBlockChecksum(const uint8* block)
if (i >= OFFSET_CHECKSUM && i < OFFSET_CHECKSUM + LENGTH_CHECKSUM) if (i >= OFFSET_CHECKSUM && i < OFFSET_CHECKSUM + LENGTH_CHECKSUM)
result += 32; result += 32;
else else
result += (uint32) block[i]; result += static_cast<uint32>(block[i]);
} }
return result; return result;
@@ -198,12 +193,10 @@ TarArchiveService::_CalculateBlockChecksum(const uint8* block)
TarArchiveService::_ReadHeader(const uint8* block, TarArchiveHeader& header) TarArchiveService::_ReadHeader(const uint8* block, TarArchiveHeader& header)
{ {
uint32 actualChecksum = _CalculateBlockChecksum(block); uint32 actualChecksum = _CalculateBlockChecksum(block);
uint32 expectedChecksum = _ReadHeaderNumeric(&block[OFFSET_CHECKSUM], uint32 expectedChecksum = _ReadHeaderNumeric(&block[OFFSET_CHECKSUM], LENGTH_CHECKSUM);
LENGTH_CHECKSUM);
if(actualChecksum != expectedChecksum) { if (actualChecksum != expectedChecksum) {
HDERROR("tar archive header has bad checksum;" HDERROR("tar archive header has bad checksum; expected %" B_PRIu32 " actual %" B_PRIu32,
"expected %" B_PRIu32 " actual %" B_PRIu32,
expectedChecksum, actualChecksum); expectedChecksum, actualChecksum);
return B_BAD_DATA; return B_BAD_DATA;
} }
@@ -212,10 +205,8 @@ TarArchiveService::_ReadHeader(const uint8* block, TarArchiveHeader& header)
_ReadHeaderString(&block[OFFSET_FILENAME], LENGTH_FILENAME, fileName); _ReadHeaderString(&block[OFFSET_FILENAME], LENGTH_FILENAME, fileName);
header.SetFileName(fileName); header.SetFileName(fileName);
header.SetLength( header.SetLength(_ReadHeaderNumeric(&block[OFFSET_LENGTH], LENGTH_LENGTH));
_ReadHeaderNumeric(&block[OFFSET_LENGTH], LENGTH_LENGTH)); header.SetFileType(_ReadHeaderFileType(block[OFFSET_FILETYPE]));
header.SetFileType(
_ReadHeaderFileType(block[OFFSET_FILETYPE]));
return B_OK; return B_OK;
} }
+80 -113
View File
@@ -1,6 +1,6 @@
/* /*
* Copyright 2013, Stephan Aßmus <superstippi@gmx.de>. * Copyright 2013, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2017-2024, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2017-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
@@ -13,19 +13,20 @@
#include <Catalog.h> #include <Catalog.h>
#include <Entry.h> #include <Entry.h>
#include <Message.h> #include <Message.h>
#include <package/PackageDefs.h>
#include <package/PackageInfo.h>
#include <package/PackageRoster.h>
#include <Path.h> #include <Path.h>
#include <Roster.h> #include <Roster.h>
#include <Screen.h> #include <Screen.h>
#include <String.h> #include <String.h>
#include <package/PackageDefs.h>
#include <package/PackageInfo.h>
#include <package/PackageRoster.h>
#include "support.h" #include "support.h"
#include "AppUtils.h" #include "AppUtils.h"
#include "Logger.h" #include "Logger.h"
#include "MainWindow.h" #include "MainWindow.h"
#include "PackageKitUtils.h"
#include "PackageUtils.h" #include "PackageUtils.h"
#include "ServerHelper.h" #include "ServerHelper.h"
#include "ServerSettings.h" #include "ServerSettings.h"
@@ -44,7 +45,7 @@ App::App()
fWindowCount(0), fWindowCount(0),
fSettingsRead(false) fSettingsRead(false)
{ {
srand((unsigned int) time(NULL)); srand(static_cast<unsigned int>(time(NULL)));
_CheckPackageDaemonRuns(); _CheckPackageDaemonRuns();
fIsFirstRun = _CheckIsFirstRun(); fIsFirstRun = _CheckIsFirstRun();
} }
@@ -62,13 +63,10 @@ App::~App()
bool bool
App::QuitRequested() App::QuitRequested()
{ {
if (fMainWindow != NULL if (fMainWindow != NULL && fMainWindow->LockLooperWithTimeout(1500000) == B_OK) {
&& fMainWindow->LockLooperWithTimeout(1500000) == B_OK) {
BMessage windowSettings; BMessage windowSettings;
fMainWindow->StoreSettings(windowSettings); fMainWindow->StoreSettings(windowSettings);
fMainWindow->UnlockLooper(); fMainWindow->UnlockLooper();
_StoreSettings(windowSettings); _StoreSettings(windowSettings);
} }
@@ -111,10 +109,8 @@ App::MessageReceived(BMessage* message)
case MSG_MAIN_WINDOW_CLOSED: case MSG_MAIN_WINDOW_CLOSED:
{ {
BMessage windowSettings; BMessage windowSettings;
if (message->FindMessage(KEY_WINDOW_SETTINGS, if (message->FindMessage(KEY_WINDOW_SETTINGS, &windowSettings) == B_OK)
&windowSettings) == B_OK) {
_StoreSettings(windowSettings); _StoreSettings(windowSettings);
}
fWindowCount--; fWindowCount--;
if (fWindowCount == 0) if (fWindowCount == 0)
@@ -199,7 +195,7 @@ app_print_help()
static arg_switch static arg_switch
app_resolve_switch(char *arg) app_resolve_switch(char* arg)
{ {
int arglen = strlen(arg); int arglen = strlen(arg);
@@ -223,18 +219,14 @@ app_resolve_switch(char *arg)
if (0 == strcmp(&arg[2], "dropcache")) if (0 == strcmp(&arg[2], "dropcache"))
return DROP_CACHE_SWITCH; return DROP_CACHE_SWITCH;
} else { } else if (arglen == 2) { // short form
if (arglen == 2) { // short form switch (arg[1]) {
switch (arg[1]) { case 'h':
case 'u': return HELP_SWITCH;
return WEB_APP_BASE_URL_SWITCH; case 'u':
return WEB_APP_BASE_URL_SWITCH;
case 'h': case 'v':
return HELP_SWITCH; return VERBOSITY_SWITCH;
case 'v':
return VERBOSITY_SWITCH;
}
} }
} }
@@ -250,15 +242,14 @@ App::ArgvReceived(int32 argc, char* argv[])
{ {
for (int i = 1; i < argc;) { for (int i = 1; i < argc;) {
// check to make sure that if there is a value for the switch, // check to make sure that if there is a value for the switch,
// that the value is in fact supplied. // that the value is in fact supplied.
switch (app_resolve_switch(argv[i])) { switch (app_resolve_switch(argv[i])) {
case VERBOSITY_SWITCH: case VERBOSITY_SWITCH:
case WEB_APP_BASE_URL_SWITCH: case WEB_APP_BASE_URL_SWITCH:
if (i == argc-1) { if (i == argc - 1) {
fprintf(stdout, "unexpected end of arguments; missing " HDERROR("unexpected end of arguments; missing value for switch [%s]", argv[i]);
"value for switch [%s]\n", argv[i]);
Quit(); Quit();
return; return;
} }
@@ -268,12 +259,12 @@ App::ArgvReceived(int32 argc, char* argv[])
break; break;
} }
// now process each switch. // now process each switch.
switch (app_resolve_switch(argv[i])) { switch (app_resolve_switch(argv[i])) {
case VERBOSITY_SWITCH: case VERBOSITY_SWITCH:
if (!Logger::SetLevelByName(argv[i+1])) { if (!Logger::SetLevelByName(argv[i + 1])) {
fprintf(stdout, "unknown log level [%s]\n", argv[i + 1]); fprintf(stdout, "unknown log level [%s]\n", argv[i + 1]);
Quit(); Quit();
} }
@@ -287,13 +278,10 @@ App::ArgvReceived(int32 argc, char* argv[])
case WEB_APP_BASE_URL_SWITCH: case WEB_APP_BASE_URL_SWITCH:
if (ServerSettings::SetBaseUrl(BUrl(argv[i + 1])) != B_OK) { if (ServerSettings::SetBaseUrl(BUrl(argv[i + 1])) != B_OK) {
fprintf(stdout, "malformed web app base url; %s\n", HDERROR("malformed web app base url; %s", argv[i + 1]);
argv[i + 1]);
Quit(); Quit();
} } else {
else { HDERROR("did configure the web base url; %s", argv[i + 1]);
fprintf(stdout, "did configure the web base url; %s\n",
argv[i + 1]);
} }
i++; // also move past the url value i++; // also move past the url value
@@ -330,8 +318,8 @@ App::ArgvReceived(int32 argc, char* argv[])
} }
/*! This method will display an alert based on a message. This message arrives /*! This method will display an alert based on a message. This message arrives
from a number of possible background threads / processes in the application. from a number of possible background threads / processes in the application.
*/ */
void void
@@ -350,8 +338,8 @@ App::_AlertSimpleError(BMessage* message)
if (message->FindInt32(KEY_ALERT_TYPE, &typeInt) != B_OK) if (message->FindInt32(KEY_ALERT_TYPE, &typeInt) != B_OK)
typeInt = B_INFO_ALERT; typeInt = B_INFO_ALERT;
BAlert* alert = new BAlert(alertTitle, alertText, B_TRANSLATE("OK"), BAlert* alert = new BAlert(alertTitle, alertText, B_TRANSLATE("OK"), NULL, NULL,
NULL, NULL, B_WIDTH_AS_USUAL, static_cast<alert_type>(typeInt)); B_WIDTH_AS_USUAL, static_cast<alert_type>(typeInt));
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go(); alert->Go();
@@ -366,7 +354,7 @@ App::_Open(const BEntry& entry)
{ {
BPath path; BPath path;
if (!entry.Exists() || entry.GetPath(&path) != B_OK) { if (!entry.Exists() || entry.GetPath(&path) != B_OK) {
fprintf(stderr, "Package file not found: %s\n", path.Path()); HDERROR("package file not found: %s", path.Path());
return; return;
} }
@@ -374,20 +362,16 @@ App::_Open(const BEntry& entry)
BPackageKit::BPackageInfo info; BPackageKit::BPackageInfo info;
status_t status = info.ReadFromPackageFile(path.Path()); status_t status = info.ReadFromPackageFile(path.Path());
if (status != B_OK) { if (status != B_OK) {
fprintf(stderr, "Failed to parse package file: %s\n", HDERROR("failed to parse package file: %s", strerror(status));
strerror(status));
return; return;
} }
// Transfer information into PackageInfo // Transfer information into PackageInfo
PackageInfoRef package(new(std::nothrow) PackageInfo(info), true); PackageInfoRef filePackageInfo = PackageKitUtils::CreatePackageInfo(info);
if (!package.IsSet()) { PackageInfoBuilder packageBuilder(filePackageInfo);
fprintf(stderr, "Could not allocate PackageInfo\n");
return;
}
PackageLocalInfoRef localInfo = PackageUtils::NewLocalInfo(package); PackageLocalInfoBuilder localInfoBuilder(filePackageInfo->LocalInfo());
localInfo->SetLocalFilePath(path.Path()); localInfoBuilder.WithLocalFilePath(path.Path());
// Set if the package is active // Set if the package is active
// //
@@ -396,33 +380,30 @@ App::_Open(const BEntry& entry)
// package kit have to know about these locations? // package kit have to know about these locations?
bool active = false; bool active = false;
BPackageKit::BPackageRoster roster; BPackageKit::BPackageRoster roster;
status = roster.IsPackageActive( status = roster.IsPackageActive(BPackageKit::B_PACKAGE_INSTALLATION_LOCATION_SYSTEM, info,
BPackageKit::B_PACKAGE_INSTALLATION_LOCATION_SYSTEM, info, &active); &active);
if (status != B_OK) { if (status != B_OK) {
fprintf(stderr, "Could not check if package was active in system: %s\n", HDERROR("could not check if package was active in system: %s", strerror(status));
strerror(status));
return; return;
} }
if (!active) { if (!active) {
status = roster.IsPackageActive( status = roster.IsPackageActive(BPackageKit::B_PACKAGE_INSTALLATION_LOCATION_HOME, info,
BPackageKit::B_PACKAGE_INSTALLATION_LOCATION_HOME, info, &active); &active);
if (status != B_OK) { if (status != B_OK) {
fprintf(stderr, HDERROR("could not check if package was active in home: %s", strerror(status));
"Could not check if package was active in home: %s\n",
strerror(status));
return; return;
} }
} }
if (active) if (active)
localInfo->SetState(ACTIVATED); localInfoBuilder.WithState(ACTIVATED);
package->SetLocalInfo(localInfo); packageBuilder.WithLocalInfo(localInfoBuilder.BuildRef());
BMessage settings; BMessage settings;
_LoadSettings(settings); _LoadSettings(settings);
MainWindow* window = new MainWindow(settings, package); MainWindow* window = new MainWindow(settings, packageBuilder.BuildRef());
_ShowWindow(window); _ShowWindow(window);
} }
@@ -478,21 +459,19 @@ App::_StoreSettings(const BMessage& settings)
// #pragma mark - // #pragma mark -
static const char* kPackageDaemonSignature static const char* kPackageDaemonSignature = "application/x-vnd.haiku-package_daemon";
= "application/x-vnd.haiku-package_daemon";
void void
App::_CheckPackageDaemonRuns() App::_CheckPackageDaemonRuns()
{ {
while (!be_roster->IsRunning(kPackageDaemonSignature)) { while (!be_roster->IsRunning(kPackageDaemonSignature)) {
BAlert* alert = new BAlert( BAlert* alert = new BAlert(B_TRANSLATE("Start package daemon"),
B_TRANSLATE("Start package daemon"),
B_TRANSLATE("HaikuDepot needs the package daemon to function, " B_TRANSLATE("HaikuDepot needs the package daemon to function, "
"and it appears to be not running.\n" "and it appears to be not running.\n"
"Would you like to start it now?"), "Would you like to start it now?"),
B_TRANSLATE("No, quit HaikuDepot"), B_TRANSLATE("No, quit HaikuDepot"), B_TRANSLATE("Start package daemon"), NULL,
B_TRANSLATE("Start package daemon"), NULL, B_WIDTH_AS_USUAL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
B_WARNING_ALERT);
alert->SetShortcut(0, B_ESCAPE); alert->SetShortcut(0, B_ESCAPE);
if (alert->Go() == 0) if (alert->Go() == 0)
@@ -509,14 +488,11 @@ App::_LaunchPackageDaemon()
{ {
status_t ret = be_roster->Launch(kPackageDaemonSignature); status_t ret = be_roster->Launch(kPackageDaemonSignature);
if (ret != B_OK) { if (ret != B_OK) {
BString errorMessage BString errorMessage = B_TRANSLATE("Starting the package daemon failed:\n\n%Error%");
= B_TRANSLATE("Starting the package daemon failed:\n\n%Error%");
errorMessage.ReplaceAll("%Error%", strerror(ret)); errorMessage.ReplaceAll("%Error%", strerror(ret));
BAlert* alert = new BAlert( BAlert* alert = new BAlert(B_TRANSLATE("Package daemon problem"), errorMessage,
B_TRANSLATE("Package daemon problem"), errorMessage, B_TRANSLATE("Quit HaikuDepot"), B_TRANSLATE("Try again"), NULL, B_WIDTH_AS_USUAL,
B_TRANSLATE("Quit HaikuDepot"),
B_TRANSLATE("Try again"), NULL, B_WIDTH_AS_USUAL,
B_WARNING_ALERT); B_WARNING_ALERT);
alert->SetShortcut(0, B_ESCAPE); alert->SetShortcut(0, B_ESCAPE);
@@ -535,20 +511,18 @@ App::_CheckIsFirstRun()
{ {
BPath testFilePath; BPath testFilePath;
bool exists = false; bool exists = false;
status_t status = StorageUtils::LocalWorkingFilesPath("testfile.txt", status_t status = StorageUtils::LocalWorkingFilesPath("testfile.txt", testFilePath, false);
testFilePath, false); if (status != B_OK)
if (status != B_OK) {
HDERROR("unable to establish the location of the test file"); HDERROR("unable to establish the location of the test file");
}
else else
status = StorageUtils::ExistsObject(testFilePath, &exists, NULL, NULL); status = StorageUtils::ExistsObject(testFilePath, &exists, NULL, NULL);
return !exists; return !exists;
} }
/*! \brief Checks to ensure that a working file is able to be written. /*! \brief Checks to ensure that a working file is able to be written.
\return false if the startup should be stopped and the application should \return false if the startup should be stopped and the application should
quit. quit.
*/ */
bool bool
@@ -556,8 +530,7 @@ App::_CheckTestFile()
{ {
BPath testFilePath; BPath testFilePath;
BString pathDescription = "???"; BString pathDescription = "???";
status_t result = StorageUtils::LocalWorkingFilesPath("testfile.txt", status_t result = StorageUtils::LocalWorkingFilesPath("testfile.txt", testFilePath, false);
testFilePath, false);
if (result == B_OK) { if (result == B_OK) {
pathDescription = testFilePath.Path(); pathDescription = testFilePath.Path();
@@ -567,19 +540,18 @@ App::_CheckTestFile()
if (result != B_OK) { if (result != B_OK) {
StorageUtils::SetWorkingFilesUnavailable(); StorageUtils::SetWorkingFilesUnavailable();
BString msg = B_TRANSLATE("This application writes and reads some" BString msg
" working files on your computer in order to function. It appears" = B_TRANSLATE("This application writes and reads some"
" that there are problems writing a test file at [%TestFilePath%]." " working files on your computer in order to function. It appears"
" Check that there are no issues with your local disk or" " that there are problems writing a test file at [%TestFilePath%]."
" permissions that might prevent this application from writing" " Check that there are no issues with your local disk or"
" files into that directory location. You may choose to acknowledge" " permissions that might prevent this application from writing"
" this problem and continue, but some functionality may be" " files into that directory location. You may choose to acknowledge"
" disabled."); " this problem and continue, but some functionality may be"
" disabled.");
msg.ReplaceAll("%TestFilePath%", pathDescription); msg.ReplaceAll("%TestFilePath%", pathDescription);
BAlert* alert = new(std::nothrow) BAlert( BAlert* alert = new(std::nothrow) BAlert(B_TRANSLATE("Problem with working files"), msg,
B_TRANSLATE("Problem with working files"),
msg,
B_TRANSLATE("Quit"), B_TRANSLATE("Continue")); B_TRANSLATE("Quit"), B_TRANSLATE("Continue"));
if (alert->Go() == 0) if (alert->Go() == 0)
@@ -611,8 +583,7 @@ App::_ClearCacheOnVersionChange()
} }
BPath lastVersionPath; BPath lastVersionPath;
if (StorageUtils::LocalWorkingFilesPath( if (StorageUtils::LocalWorkingFilesPath("version.txt", lastVersionPath) != B_OK) {
"version.txt", lastVersionPath) != B_OK) {
HDERROR("clear cache; unable to get version file path"); HDERROR("clear cache; unable to get version file path");
return; return;
} }
@@ -620,30 +591,26 @@ App::_ClearCacheOnVersionChange()
bool exists; bool exists;
off_t size; off_t size;
if (StorageUtils::ExistsObject( if (StorageUtils::ExistsObject(lastVersionPath, &exists, NULL, &size) != B_OK) {
lastVersionPath, &exists, NULL, &size) != B_OK) {
HDERROR("clear cache; unable to check version file exists"); HDERROR("clear cache; unable to check version file exists");
return; return;
} }
BString lastVersion; BString lastVersion;
if (exists && StorageUtils::AppendToString(lastVersionPath, lastVersion) if (exists && StorageUtils::AppendToString(lastVersionPath, lastVersion) != B_OK) {
!= B_OK) { HDERROR("clear cache; unable to read the version from [%s]", lastVersionPath.Path());
HDERROR("clear cache; unable to read the version from [%s]",
lastVersionPath.Path());
return; return;
} }
if (lastVersion != version) { if (lastVersion != version) {
HDINFO("last version [%s] and current version [%s] do not match" HDINFO("last version [%s] and current version [%s] do not match -> will flush cache",
" -> will flush cache", lastVersion.String(), version.String()); lastVersion.String(), version.String());
StorageUtils::RemoveWorkingDirectoryContents(); StorageUtils::RemoveWorkingDirectoryContents();
HDINFO("will write version [%s] to [%s]", HDINFO("will write version [%s] to [%s]", version.String(), lastVersionPath.Path());
version.String(), lastVersionPath.Path());
StorageUtils::AppendToFile(version, lastVersionPath); StorageUtils::AppendToFile(version, lastVersionPath);
} else { } else {
HDINFO("last version [%s] and current version [%s] match" HDINFO("last version [%s] and current version [%s] match -> cache retained",
" -> cache retained", lastVersion.String(), version.String()); lastVersion.String(), version.String());
} }
} }
+179 -152
View File
@@ -1,7 +1,7 @@
/* /*
* Copyright 2013-214, Stephan Aßmus <superstippi@gmx.de>. * Copyright 2013-214, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>. * Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>.
* Copyright 2020-2024, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2020-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
@@ -26,7 +26,6 @@
#include "Logger.h" #include "Logger.h"
#include "MainWindow.h" #include "MainWindow.h"
#include "MarkupTextView.h" #include "MarkupTextView.h"
#include "MessagePackageListener.h"
#include "PackageUtils.h" #include "PackageUtils.h"
#include "RatingUtils.h" #include "RatingUtils.h"
#include "RatingView.h" #include "RatingView.h"
@@ -55,8 +54,7 @@
// #pragma mark - PackageView // #pragma mark - PackageView
class StackedFeaturesPackageBandMetrics class StackedFeaturesPackageBandMetrics {
{
public: public:
StackedFeaturesPackageBandMetrics(float width, BFont* titleFont, BFont* metadataFont) StackedFeaturesPackageBandMetrics(float width, BFont* titleFont, BFont* metadataFont)
{ {
@@ -69,8 +67,7 @@ public:
metadataFont->GetHeight(&metadataFontHeight); metadataFont->GetHeight(&metadataFontHeight);
float totalTitleAndMetadataHeight = titleFontHeight.ascent + titleFontHeight.descent float totalTitleAndMetadataHeight = titleFontHeight.ascent + titleFontHeight.descent
+ titleFontHeight.leading + titleFontHeight.leading + metadataFontHeight.leading
+ metadataFontHeight.leading
+ 2.0 * (metadataFontHeight.ascent + metadataFontHeight.descent); + 2.0 * (metadataFontHeight.ascent + metadataFontHeight.descent);
fHeight = fmaxf(totalTitleAndMetadataHeight, iconSize.Height()) + 2.0 * padding; fHeight = fmaxf(totalTitleAndMetadataHeight, iconSize.Height()) + 2.0 * padding;
@@ -85,41 +82,43 @@ public:
float titleWidthM = titleFont->StringWidth("M"); float titleWidthM = titleFont->StringWidth("M");
float leftTitlePublisherAndChronologicalInfo = fIconRect.right + padding; float leftTitlePublisherAndChronologicalInfo = fIconRect.right + padding;
float rightTitlePublisherAndChronologicalInfo = fminf(width, fIconRect.Size().Width() float rightTitlePublisherAndChronologicalInfo = fminf(width,
+ (2.0 * padding) + (titleWidthM * M_COUNT_TITLE)); fIconRect.Size().Width() + (2.0 * padding) + (titleWidthM * M_COUNT_TITLE));
// left, top, right bottom
// left, top, right bottom
fTitleRect = BRect(leftTitlePublisherAndChronologicalInfo, fTitleRect = BRect(leftTitlePublisherAndChronologicalInfo,
(fHeight - totalTitleAndMetadataHeight) / 2.0, (fHeight - totalTitleAndMetadataHeight) / 2.0,
rightTitlePublisherAndChronologicalInfo, rightTitlePublisherAndChronologicalInfo,
((fHeight - totalTitleAndMetadataHeight) / 2.0) ((fHeight - totalTitleAndMetadataHeight) / 2.0) + titleFontHeight.ascent
+ titleFontHeight.ascent + titleFontHeight.descent); + titleFontHeight.descent);
fPublisherRect = BRect(leftTitlePublisherAndChronologicalInfo, fPublisherRect = BRect(leftTitlePublisherAndChronologicalInfo,
fTitleRect.bottom + titleFontHeight.leading, fTitleRect.bottom + titleFontHeight.leading,
rightTitlePublisherAndChronologicalInfo, rightTitlePublisherAndChronologicalInfo,
fTitleRect.bottom + titleFontHeight.leading fTitleRect.bottom + titleFontHeight.leading + metadataFontHeight.ascent
+ metadataFontHeight.ascent + metadataFontHeight.descent); + metadataFontHeight.descent);
fChronologicalInfoRect = BRect(leftTitlePublisherAndChronologicalInfo, fChronologicalInfoRect = BRect(leftTitlePublisherAndChronologicalInfo,
fPublisherRect.bottom + metadataFontHeight.leading, fPublisherRect.bottom + metadataFontHeight.leading,
rightTitlePublisherAndChronologicalInfo, rightTitlePublisherAndChronologicalInfo,
fPublisherRect.bottom + metadataFontHeight.leading fPublisherRect.bottom + metadataFontHeight.leading + metadataFontHeight.ascent
+ metadataFontHeight.ascent + metadataFontHeight.descent); + metadataFontHeight.descent);
} }
// sort out the ratings display // sort out the ratings display
{ {
BSize ratingStarSize = SharedIcons::IconStarBlue16Scaled()->Bitmap()->Bounds().Size(); BSize ratingStarSize = SharedIcons::IconStarBlue16Scaled()->Bitmap()->Bounds().Size();
RatingStarsMetrics ratingStarsMetrics(ratingStarSize); RatingStarsMetrics ratingStarsMetrics(ratingStarSize);
fRatingStarsRect = BRect(BPoint(fTitleRect.right + padding, fRatingStarsRect = BRect(BPoint(fTitleRect.right + padding,
(fHeight - ratingStarsMetrics.Size().Height()) / 2), ratingStarsMetrics.Size()); (fHeight - ratingStarsMetrics.Size().Height()) / 2),
ratingStarsMetrics.Size());
if (fRatingStarsRect.right > width) if (fRatingStarsRect.right > width) {
fRatingStarsRect = BRect(); fRatingStarsRect = BRect();
else { } else {
// Now sort out the position for the summary. This is reckoned as a container // Now sort out the position for the summary. This is reckoned as a container
// rect because it would be nice to layout the text with newlines and not just a // rect because it would be nice to layout the text with newlines and not just a
// single line. // single line.
@@ -190,10 +189,7 @@ public:
: :
BView("stacked featured packages view", B_WILL_DRAW | B_FRAME_EVENTS), BView("stacked featured packages view", B_WILL_DRAW | B_FRAME_EVENTS),
fModel(model), fModel(model),
fSelectedIndex(-1), fSelectedIndex(-1)
fPackageListener(
new(std::nothrow) OnePackageMessagePackageListener(this)),
fLowestIndexAddedOrRemoved(-1)
{ {
SetEventMask(B_POINTER_EVENTS); SetEventMask(B_POINTER_EVENTS);
@@ -208,29 +204,17 @@ public:
virtual ~StackedFeaturedPackagesView() virtual ~StackedFeaturedPackagesView()
{ {
fPackageListener->SetPackage(PackageInfoRef(NULL));
fPackageListener->ReleaseReference();
delete fBandMetrics; delete fBandMetrics;
delete fTitleFont; delete fTitleFont;
delete fMetadataFont; delete fMetadataFont;
delete fSummaryFont; delete fSummaryFont;
} }
// #pragma mark - message handling and events // #pragma mark - message handling and events
virtual void MessageReceived(BMessage* message) virtual void MessageReceived(BMessage* message)
{ {
switch (message->what) { switch (message->what) {
case MSG_UPDATE_PACKAGE:
{
BString name;
if (message->FindString("name", &name) != B_OK)
HDINFO("expected 'name' key on package update message");
else
_HandleUpdatePackage(name);
break;
}
case B_COLORS_UPDATED: case B_COLORS_UPDATED:
{ {
Invalidate(); Invalidate();
@@ -268,16 +252,14 @@ public:
case B_DOWN_ARROW: case B_DOWN_ARROW:
{ {
int32 lastIndex = static_cast<int32>(fPackages.size()) - 1; int32 lastIndex = static_cast<int32>(fPackages.size()) - 1;
if (!IsEmpty() && fSelectedIndex != -1 if (!IsEmpty() && fSelectedIndex != -1 && fSelectedIndex < lastIndex)
&& fSelectedIndex < lastIndex) {
_MessageSelectIndex(fSelectedIndex + 1); _MessageSelectIndex(fSelectedIndex + 1);
}
break; break;
} }
case B_LEFT_ARROW: case B_LEFT_ARROW:
case B_UP_ARROW: case B_UP_ARROW:
if (fSelectedIndex > 0) if (fSelectedIndex > 0)
_MessageSelectIndex( fSelectedIndex - 1); _MessageSelectIndex(fSelectedIndex - 1);
break; break;
case B_PAGE_UP: case B_PAGE_UP:
{ {
@@ -328,13 +310,26 @@ public:
} }
// #pragma mark - update / add / remove / clear data // #pragma mark - update / add / remove / clear data
void UpdatePackage(uint32 changeMask, const PackageInfoRef& package) void HandlePackagesChanged(const PackageInfoEvents& events)
{ {
// TODO; could optimize the invalidation? for (int32 i = events.CountEvents() - 1; i >= 0; i--)
_HandlePackageChanged(events.EventAtIndex(i));
}
void _HandlePackageChanged(const PackageInfoEvent& event)
{
uint32 changes = event.Changes();
PackageInfoRef package = event.Package();
if (!package.IsSet() || 0 == changes)
return;
int32 index = _IndexOfPackage(package); int32 index = _IndexOfPackage(package);
if (index >= 0) { if (index >= 0) {
fPackages[index] = package; fPackages[index] = package;
Invalidate(_RectOfIndex(index)); Invalidate(_RectOfIndex(index));
@@ -344,10 +339,6 @@ public:
void Clear() void Clear()
{ {
for (std::vector<PackageInfoRef>::iterator it = fPackages.begin();
it != fPackages.end(); it++) {
(*it)->RemoveListener(fPackageListener);
}
fPackages.clear(); fPackages.clear();
fSelectedIndex = -1; fSelectedIndex = -1;
@@ -367,8 +358,8 @@ public:
} }
static BFont* CreateMetadataFont() static BFont* CreateMetadataFont()
{ {
BFont* font = new BFont(be_plain_font); BFont* font = new BFont(be_plain_font);
font_family family; font_family family;
font_style style; font_style style;
@@ -396,14 +387,6 @@ public:
} }
void _HandleUpdatePackage(const BString& name)
{
int32 index = _IndexOfName(name);
if (index != -1)
Invalidate(_RectOfIndex(index));
}
static int _CmpProminences(int64 a, int64 b) static int _CmpProminences(int64 a, int64 b)
{ {
if (a <= 0) if (a <= 0)
@@ -456,68 +439,110 @@ public:
} }
void BeginAddRemove() void _InvalidateFromIndex(int32 index)
{ {
fLowestIndexAddedOrRemoved = INT32_MAX; if (index != -1) {
} if (fPackages.empty()) {
void EndAddRemove()
{
if (fLowestIndexAddedOrRemoved < INT32_MAX) {
if (fPackages.empty())
Invalidate(); Invalidate();
else { } else {
BRect invalidRect = Bounds(); BRect invalidRect = Bounds();
invalidRect.top = _YOfIndex(fLowestIndexAddedOrRemoved); invalidRect.top = _YOfIndex(index);
Invalidate(invalidRect); Invalidate(invalidRect);
} }
} }
} }
void AddPackage(const PackageInfoRef& package) /*! Finds the index of the first difference between the current list of
packages and the provided list of packages. Returns -1 if there is
no difference. This is used with the `RetainPackages` method.
*/
int32 _IndexOfFirstDifference(const std::vector<PackageInfoRef>& packages)
{
size_t result = 0;
size_t packagesLen = packages.size();
size_t currentPackagesLen = fPackages.size();
while (true) {
if (result == packagesLen && result == currentPackagesLen)
return -1;
if (result >= packagesLen || result >= currentPackagesLen)
return static_cast<int32>(result);
if (packages[result] != fPackages[result])
return static_cast<int32>(result);
result++;
}
}
void RetainPackages(const std::vector<PackageInfoRef>& packages)
{
int32 lowestIndexAddedOrRemoved = _IndexOfFirstDifference(packages);
fPackages = packages;
_InvalidateFromIndex(lowestIndexAddedOrRemoved);
}
void _AddPackage(const PackageInfoRef& package, int32* lowestIndexAddedOrRemoved)
{ {
// fPackages is sorted and for this reason it is possible to find the // fPackages is sorted and for this reason it is possible to find the
// insertion point by identifying the first item in fPackages that does // insertion point by identifying the first item in fPackages that does
// not return true from the method '_IsPackageBefore'. // not return true from the method '_IsPackageBefore'.
std::vector<PackageInfoRef>::iterator itInsertionPt std::vector<PackageInfoRef>::iterator itInsertionPt
= std::lower_bound(fPackages.begin(), fPackages.end(), package, = std::lower_bound(fPackages.begin(), fPackages.end(), package, &_IsPackageBefore);
&_IsPackageBefore);
if (itInsertionPt == fPackages.end() || package->Name() != (*itInsertionPt)->Name()) {
int32 insertionIndex = std::distance<std::vector<PackageInfoRef>::const_iterator>(
fPackages.begin(), itInsertionPt);
if (itInsertionPt == fPackages.end()
|| package->Name() != (*itInsertionPt)->Name()) {
int32 insertionIndex =
std::distance<std::vector<PackageInfoRef>::const_iterator>(
fPackages.begin(), itInsertionPt);
if (fSelectedIndex >= insertionIndex) if (fSelectedIndex >= insertionIndex)
fSelectedIndex++; fSelectedIndex++;
fPackages.insert(itInsertionPt, package); fPackages.insert(itInsertionPt, package);
package->AddListener(fPackageListener);
if (insertionIndex < fLowestIndexAddedOrRemoved) if (insertionIndex > *lowestIndexAddedOrRemoved)
fLowestIndexAddedOrRemoved = insertionIndex; *lowestIndexAddedOrRemoved = insertionIndex;
} }
} }
void RemovePackage(const PackageInfoRef& package) void _RemovePackage(const PackageInfoRef& package, int32* lowestIndexAddedOrRemoved)
{ {
int32 index = _IndexOfPackage(package); int32 removalIndex = _IndexOfPackage(package);
if (index >= 0) { if (removalIndex >= 0) {
if (fSelectedIndex == index)
if (fSelectedIndex == removalIndex)
fSelectedIndex = -1; fSelectedIndex = -1;
if (fSelectedIndex > index)
if (fSelectedIndex > removalIndex)
fSelectedIndex--; fSelectedIndex--;
fPackages[index]->RemoveListener(fPackageListener);
fPackages.erase(fPackages.begin() + index); fPackages.erase(fPackages.begin() + removalIndex);
if (index < fLowestIndexAddedOrRemoved)
fLowestIndexAddedOrRemoved = index; if (removalIndex > *lowestIndexAddedOrRemoved)
*lowestIndexAddedOrRemoved = removalIndex;
} }
} }
// #pragma mark - selection and index handling void AddRemovePackages(const std::vector<PackageInfoRef>& addedPackages,
const std::vector<PackageInfoRef>& removedPackages)
{
int32 lowestIndexAddedOrRemoved = -1;
std::vector<PackageInfoRef>::const_iterator it;
for (it = addedPackages.begin(); it != addedPackages.end(); it++)
_AddPackage(*it, &lowestIndexAddedOrRemoved);
for (it = removedPackages.begin(); it != removedPackages.end(); it++)
_RemovePackage(*it, &lowestIndexAddedOrRemoved);
_InvalidateFromIndex(lowestIndexAddedOrRemoved);
}
// #pragma mark - selection and index handling
void SelectPackage(const PackageInfoRef& package) void SelectPackage(const PackageInfoRef& package)
@@ -543,11 +568,12 @@ public:
int32 _IndexOfPackage(PackageInfoRef package) const int32 _IndexOfPackage(PackageInfoRef package) const
{ {
std::vector<PackageInfoRef>::const_iterator it std::vector<PackageInfoRef>::const_iterator it
= std::lower_bound(fPackages.begin(), fPackages.end(), package, = std::lower_bound(fPackages.begin(), fPackages.end(), package, &_IsPackageBefore);
&_IsPackageBefore);
return (it == fPackages.end() || (*it)->Name() != package->Name()) if (it == fPackages.end() || (*it)->Name() != package->Name())
? -1 : it - fPackages.begin(); return -1;
return it - fPackages.begin();
} }
@@ -564,7 +590,7 @@ public:
} }
// #pragma mark - drawing and rendering // #pragma mark - drawing and rendering
virtual void Draw(BRect updateRect) virtual void Draw(BRect updateRect)
@@ -626,9 +652,14 @@ public:
if (!iconRect.IsValid()) if (!iconRect.IsValid())
return; return;
PackageIconRepositoryRef iconRepository = fModel.IconRepository();
if (!iconRepository.IsSet())
return;
BitmapHolderRef icon; BitmapHolderRef icon;
status_t iconResult = fModel.GetPackageIconRepository().GetIcon(pkg->Name(),
iconRect.Width(), icon); status_t iconResult = iconRepository->GetIcon(pkg->Name(), iconRect.Width(), icon);
if (iconResult == B_OK) { if (iconResult == B_OK) {
if (icon.IsSet()) { if (icon.IsSet()) {
@@ -659,9 +690,9 @@ public:
float trailingIconsWidth = 0.0f; float trailingIconsWidth = 0.0f;
for (std::vector<BitmapHolderRef>::iterator it = trailingIconBitmaps.begin(); for (std::vector<BitmapHolderRef>::iterator it = trailingIconBitmaps.begin();
it != trailingIconBitmaps.end(); it++) { it != trailingIconBitmaps.end(); it++) {
trailingIconsWidth += (*it)->Bitmap()->Bounds().Width() trailingIconsWidth
* (1.0 + TRAILING_ICON_PADDING_LEFT_FACTOR); += (*it)->Bitmap()->Bounds().Width() * (1.0 + TRAILING_ICON_PADDING_LEFT_FACTOR);
} }
SetDrawingMode(B_OP_COPY); SetDrawingMode(B_OP_COPY);
@@ -671,20 +702,20 @@ public:
float titleRightTrailingIconsPadding = 0.0f; float titleRightTrailingIconsPadding = 0.0f;
if (!trailingIconBitmaps.empty()) { if (!trailingIconBitmaps.empty()) {
titleRightTrailingIconsPadding = StringWidth("M") titleRightTrailingIconsPadding
* TITLE_RIGHT_TRAILING_ICON_PADDING_M_FACTOR; = StringWidth("M") * TITLE_RIGHT_TRAILING_ICON_PADDING_M_FACTOR;
} }
font_height fontHeight; font_height fontHeight;
fTitleFont->GetHeight(&fontHeight); fTitleFont->GetHeight(&fontHeight);
BPoint pt = textRect.LeftTop() + BPoint(0.0, + fontHeight.ascent); BPoint pt = textRect.LeftTop() + BPoint(0.0, +fontHeight.ascent);
BString title; BString title;
PackageUtils::TitleOrName(pkg, title); PackageUtils::TitleOrName(pkg, title);
BString renderedText = title; BString renderedText = title;
TruncateString(&renderedText, B_TRUNCATE_END, textRect.Width() TruncateString(&renderedText, B_TRUNCATE_END,
- (titleRightTrailingIconsPadding + trailingIconsWidth)); textRect.Width() - (titleRightTrailingIconsPadding + trailingIconsWidth));
DrawString(renderedText, pt); DrawString(renderedText, pt);
@@ -697,14 +728,15 @@ public:
SetDrawingMode(B_OP_ALPHA); SetDrawingMode(B_OP_ALPHA);
for (std::vector<BitmapHolderRef>::iterator it = trailingIconBitmaps.begin(); for (std::vector<BitmapHolderRef>::iterator it = trailingIconBitmaps.begin();
it != trailingIconBitmaps.end(); it++) { it != trailingIconBitmaps.end(); it++) {
const BBitmap* bitmap = (*it)->Bitmap(); const BBitmap* bitmap = (*it)->Bitmap();
BRect bitmapBounds = bitmap->Bounds(); BRect bitmapBounds = bitmap->Bounds();
float trailingIconTopLeftPtX = ceilf( float trailingIconTopLeftPtX
trailingIconX + (bitmapBounds.Width() * TRAILING_ICON_PADDING_LEFT_FACTOR)) + 0.5; = ceilf(trailingIconX + (bitmapBounds.Width() * TRAILING_ICON_PADDING_LEFT_FACTOR))
float trailingIconTopLeftPtY = ceilf( + 0.5;
trailingIconMidY - (bitmapBounds.Height() / 2.0)) + 0.5; float trailingIconTopLeftPtY
= ceilf(trailingIconMidY - (bitmapBounds.Height() / 2.0)) + 0.5;
BRect trailingIconRect(BPoint(trailingIconTopLeftPtX, trailingIconTopLeftPtY), BRect trailingIconRect(BPoint(trailingIconTopLeftPtX, trailingIconTopLeftPtY),
bitmap->Bounds().Size()); bitmap->Bounds().Size());
@@ -727,7 +759,7 @@ public:
font_height fontHeight; font_height fontHeight;
fMetadataFont->GetHeight(&fontHeight); fMetadataFont->GetHeight(&fontHeight);
BPoint pt = textRect.LeftTop() + BPoint(0.0, + fontHeight.ascent); BPoint pt = textRect.LeftTop() + BPoint(0.0, +fontHeight.ascent);
BString renderedText(text); BString renderedText(text);
TruncateString(&renderedText, B_TRUNCATE_END, textRect.Width()); TruncateString(&renderedText, B_TRUNCATE_END, textRect.Width());
@@ -762,7 +794,7 @@ public:
if (!ratingRect.IsValid()) if (!ratingRect.IsValid())
return; return;
UserRatingInfoRef userRatingInfo = pkg->UserRatingInfo(); PackageUserRatingInfoRef userRatingInfo = pkg->UserRatingInfo();
if (userRatingInfo.IsSet()) { if (userRatingInfo.IsSet()) {
UserRatingSummaryRef userRatingSummary = userRatingInfo->Summary(); UserRatingSummaryRef userRatingSummary = userRatingInfo->Summary();
@@ -789,9 +821,10 @@ public:
// The text rect is a container into which later text can be made to flow multi-line. For // The text rect is a container into which later text can be made to flow multi-line. For
// now just draw one line of the summary. // now just draw one line of the summary.
BPoint pt = textRect.LeftTop() + BPoint(0.0, BPoint pt = textRect.LeftTop()
(textRect.Size().Height() / 2.0) - ((fontHeight.ascent + fontHeight.descent) / 2.0) + BPoint(0.0,
+ fontHeight.ascent); (textRect.Size().Height() / 2.0) - ((fontHeight.ascent + fontHeight.descent) / 2.0)
+ fontHeight.ascent);
BString summary; BString summary;
PackageUtils::Summary(pkg, summary); PackageUtils::Summary(pkg, summary);
@@ -801,7 +834,7 @@ public:
} }
// #pragma mark - geometry and scrolling // #pragma mark - geometry and scrolling
/*! This method will make sure that the package at the given index is /*! This method will make sure that the package at the given index is
@@ -816,9 +849,9 @@ public:
if (!_IsIndexEntirelyVisible(index)) { if (!_IsIndexEntirelyVisible(index)) {
BRect bounds = Bounds(); BRect bounds = Bounds();
int32 indexOfCentreVisible = _IndexOfY(bounds.top + bounds.Height() / 2); int32 indexOfCentreVisible = _IndexOfY(bounds.top + bounds.Height() / 2);
if (index < indexOfCentreVisible) if (index < indexOfCentreVisible) {
ScrollTo(0, _YOfIndex(index)); ScrollTo(0, _YOfIndex(index));
else { } else {
float scrollPointY = (_YOfIndex(index) + fBandMetrics->Height()) - bounds.Height(); float scrollPointY = (_YOfIndex(index) + fBandMetrics->Height()) - bounds.Height();
ScrollTo(0, scrollPointY); ScrollTo(0, scrollPointY);
} }
@@ -902,7 +935,7 @@ public:
int32 i = static_cast<int32>(y / fBandMetrics->Height()); int32 i = static_cast<int32>(y / fBandMetrics->Height());
if (i < 0) if (i < 0)
return 0; return 0;
return std::min(i, (int32) (fPackages.size() - 1)); return std::min(i, (int32)(fPackages.size() - 1));
} }
@@ -918,9 +951,6 @@ private:
std::vector<PackageInfoRef> std::vector<PackageInfoRef>
fPackages; fPackages;
int32 fSelectedIndex; int32 fSelectedIndex;
OnePackageMessagePackageListener*
fPackageListener;
int32 fLowestIndexAddedOrRemoved;
StackedFeaturesPackageBandMetrics* StackedFeaturesPackageBandMetrics*
fBandMetrics; fBandMetrics;
@@ -940,11 +970,10 @@ FeaturedPackagesView::FeaturedPackagesView(Model& model)
{ {
fPackagesView = new StackedFeaturedPackagesView(fModel); fPackagesView = new StackedFeaturedPackagesView(fModel);
fScrollView = new BScrollView("featured packages scroll view", fScrollView = new BScrollView("featured packages scroll view", fPackagesView, 0, false, true,
fPackagesView, 0, false, true, B_FANCY_BORDER); B_FANCY_BORDER);
BLayoutBuilder::Group<>(this) BLayoutBuilder::Group<>(this).Add(fScrollView, 1.0f);
.Add(fScrollView, 1.0f);
} }
@@ -954,35 +983,21 @@ FeaturedPackagesView::~FeaturedPackagesView()
void void
FeaturedPackagesView::BeginAddRemove() FeaturedPackagesView::RetainPackages(const std::vector<PackageInfoRef>& packages)
{ {
fPackagesView->BeginAddRemove(); fPackagesView->RetainPackages(packages);
}
void
FeaturedPackagesView::EndAddRemove()
{
fPackagesView->EndAddRemove();
_AdjustViews(); _AdjustViews();
} }
/*! This method will add the package into the list to be displayed. The
insertion will occur in alphabetical order.
*/
void void
FeaturedPackagesView::AddPackage(const PackageInfoRef& package) FeaturedPackagesView::AddRemovePackages(const std::vector<PackageInfoRef>& addedPackages,
const std::vector<PackageInfoRef>& removedPackages)
{ {
fPackagesView->AddPackage(package); if (!addedPackages.empty() || !removedPackages.empty()) {
} fPackagesView->AddRemovePackages(addedPackages, removedPackages);
_AdjustViews();
}
void
FeaturedPackagesView::RemovePackage(const PackageInfoRef& package)
{
fPackagesView->RemovePackage(package);
} }
@@ -996,8 +1011,7 @@ FeaturedPackagesView::Clear()
void void
FeaturedPackagesView::SelectPackage(const PackageInfoRef& package, FeaturedPackagesView::SelectPackage(const PackageInfoRef& package, bool scrollToEntry)
bool scrollToEntry)
{ {
fPackagesView->SelectPackage(package); fPackagesView->SelectPackage(package);
@@ -1017,9 +1031,22 @@ FeaturedPackagesView::DoLayout()
} }
void
FeaturedPackagesView::HandleIconsChanged()
{
Invalidate();
}
void
FeaturedPackagesView::HandlePackagesChanged(const PackageInfoEvents& events)
{
fPackagesView->HandlePackagesChanged(events);
}
void void
FeaturedPackagesView::_AdjustViews() FeaturedPackagesView::_AdjustViews()
{ {
fScrollView->FrameResized(fScrollView->Frame().Width(), fScrollView->FrameResized(fScrollView->Frame().Width(), fScrollView->Frame().Height());
fScrollView->Frame().Height());
} }
+11 -5
View File
@@ -1,12 +1,14 @@
/* /*
* Copyright 2014, Stephan Aßmus <superstippi@gmx.de>. * Copyright 2014, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2020-2024, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2020-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef FEATURED_PACKAGES_VIEW_H #ifndef FEATURED_PACKAGES_VIEW_H
#define FEATURED_PACKAGES_VIEW_H #define FEATURED_PACKAGES_VIEW_H
#include <vector>
#include <View.h> #include <View.h>
#include "Model.h" #include "Model.h"
@@ -24,17 +26,21 @@ public:
virtual void DoLayout(); virtual void DoLayout();
void BeginAddRemove(); void RetainPackages(const std::vector<PackageInfoRef>& packages);
void EndAddRemove(); void AddRemovePackages(const std::vector<PackageInfoRef>& addedPackages,
void AddPackage(const PackageInfoRef& package); const std::vector<PackageInfoRef>& removedPackages);
void RemovePackage(const PackageInfoRef& package);
void Clear(); void Clear();
void SelectPackage(const PackageInfoRef& package, void SelectPackage(const PackageInfoRef& package,
bool scrollToEntry = false); bool scrollToEntry = false);
void HandleIconsChanged();
void HandlePackagesChanged(const PackageInfoEvents& events);
private: private:
void _AdjustViews(); void _AdjustViews();
void _HandlePackageChanged(const PackageInfoEvent& event);
private: private:
Model& fModel; Model& fModel;
+18 -22
View File
@@ -1,6 +1,6 @@
/* /*
* Copyright 2013, Stephan Aßmus <superstippi@gmx.de>. * Copyright 2013, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2019-2020, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2019-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
@@ -36,15 +36,12 @@ FilterView::FilterView()
fShowField = new BMenuField("category", B_TRANSLATE("Category:"), showMenu); fShowField = new BMenuField("category", B_TRANSLATE("Category:"), showMenu);
// Construct search terms field // Construct search terms field
fSearchTermsText = new BTextControl("search terms", fSearchTermsText = new BTextControl("search terms", B_TRANSLATE("Search terms:"), "", NULL);
B_TRANSLATE("Search terms:"), "", NULL); fSearchTermsText->SetModificationMessage(new BMessage(MSG_SEARCH_TERMS_MODIFIED));
fSearchTermsText->SetModificationMessage(
new BMessage(MSG_SEARCH_TERMS_MODIFIED));
BSize minSearchSize = fSearchTermsText->MinSize(); BSize minSearchSize = fSearchTermsText->MinSize();
float minSearchWidth float minSearchWidth = be_plain_font->StringWidth(fSearchTermsText->Label())
= be_plain_font->StringWidth(fSearchTermsText->Label()) + be_plain_font->StringWidth("XXX") * 6;
+ be_plain_font->StringWidth("XXX") * 6;
minSearchWidth = std::max(minSearchSize.width, minSearchWidth); minSearchWidth = std::max(minSearchSize.width, minSearchWidth);
minSearchSize.width = minSearchWidth; minSearchSize.width = minSearchWidth;
fSearchTermsText->SetExplicitMinSize(minSearchSize); fSearchTermsText->SetExplicitMinSize(minSearchSize);
@@ -107,27 +104,25 @@ FilterView::AdoptModel(Model& model)
BMenu* showMenu = fShowField->Menu(); BMenu* showMenu = fShowField->Menu();
showMenu->RemoveItems(0, showMenu->CountItems(), true); showMenu->RemoveItems(0, showMenu->CountItems(), true);
showMenu->AddItem(new BMenuItem(B_TRANSLATE("All categories"), showMenu->AddItem(
new BMessage(MSG_CATEGORY_SELECTED))); new BMenuItem(B_TRANSLATE("All categories"), new BMessage(MSG_CATEGORY_SELECTED)));
AutoLocker<BLocker> locker(model.Lock()); if (model.HasCategories()) {
int32 categoryCount = model.CountCategories();
if (categoryCount > 0) {
showMenu->AddItem(new BSeparatorItem()); showMenu->AddItem(new BSeparatorItem());
_AddCategoriesToMenu(model, showMenu); _AddCategoriesToMenu(model, showMenu);
showMenu->SetEnabled(true);
} else {
showMenu->SetEnabled(false);
} }
showMenu->SetEnabled(categoryCount > 0); if (!_SelectCategoryCode(showMenu, model.FilterSpecification()->Category()))
if (!_SelectCategoryCode(showMenu, model.PackageFilter()->Category()))
showMenu->ItemAt(0)->SetMarked(true); showMenu->ItemAt(0)->SetMarked(true);
} }
/*! Tries to mark the menu item that corresponds to the supplied /*! Tries to mark the menu item that corresponds to the supplied
category code. If the supplied code was found and the item marked category code. If the supplied code was found and the item marked
then the method will return true. then the method will return true.
*/ */
/*static*/ bool /*static*/ bool
@@ -159,9 +154,10 @@ FilterView::_MatchesCategoryCode(BMenuItem* item, const BString& code)
/*static*/ void /*static*/ void
FilterView::_AddCategoriesToMenu(Model& model, BMenu* menu) FilterView::_AddCategoriesToMenu(Model& model, BMenu* menu)
{ {
int count = model.CountCategories(); std::vector<CategoryRef> categories = model.Categories();
for (int i = 0; i < count; i++) { std::vector<CategoryRef>::const_iterator it;
const CategoryRef& category = model.CategoryAtIndex(i); for (it = categories.begin(); it != categories.end(); it++) {
const CategoryRef& category = *it;
BMessage* message = new BMessage(MSG_CATEGORY_SELECTED); BMessage* message = new BMessage(MSG_CATEGORY_SELECTED);
message->AddString("code", category->Code()); message->AddString("code", category->Code());
BMenuItem* item = new BMenuItem(category->Name(), message); BMenuItem* item = new BMenuItem(category->Name(), message);
File diff suppressed because it is too large Load Diff
+13 -6
View File
@@ -2,7 +2,7 @@
* Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>. * Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2013, Rene Gollent <rene@gollent.com>. * Copyright 2013, Rene Gollent <rene@gollent.com>.
* Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>. * Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>.
* Copyright 2017-2024, Andrew Lindesay <apl@lindesay.co.nz>. * Copyright 2017-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*/ */
#ifndef MAIN_WINDOW_H #ifndef MAIN_WINDOW_H
@@ -41,7 +41,7 @@ class MainWindow :
public: public:
MainWindow(const BMessage& settings); MainWindow(const BMessage& settings);
MainWindow(const BMessage& settings, MainWindow(const BMessage& settings,
PackageInfoRef& package); const PackageInfoRef package);
virtual ~MainWindow(); virtual ~MainWindow();
// BWindow interface // BWindow interface
@@ -63,12 +63,13 @@ public:
const UserDetail& userDetail); const UserDetail& userDetail);
// services PackageInfoListener via MainWindowPackageInfoListener // services PackageInfoListener via MainWindowPackageInfoListener
void PackageChanged( void PackagesChanged(const PackageInfoEvents& events);
const PackageInfoEvent& event);
private: private:
static const BString _WindowTitleForPackage(const PackageInfoRef& pkg); static const BString _WindowTitleForPackage(const PackageInfoRef& pkg);
const std::vector<PackageInfoRef>
_CreateSnapshotOfFilteredPackages();
std::vector<DepotInfoRef> std::vector<DepotInfoRef>
_CreateSnapshotOfDepots(); _CreateSnapshotOfDepots();
@@ -95,8 +96,6 @@ private:
void _AdoptModelControls(); void _AdoptModelControls();
void _AdoptModel(); void _AdoptModel();
void _AddRemovePackageFromLists(
const PackageInfoRef& package);
void _AdoptPackage(const PackageInfoRef& package); void _AdoptPackage(const PackageInfoRef& package);
void _ClearPackage(); void _ClearPackage();
@@ -128,6 +127,9 @@ private:
void _HandleProcessCoordinatorChanged( void _HandleProcessCoordinatorChanged(
ProcessCoordinatorState& coordinatorState); ProcessCoordinatorState& coordinatorState);
void _HandlePackagesChanged(const BMessage* message);
void _HandlePackagesChanged(const PackageInfoEvents& events);
static status_t _RefreshModelThreadWorker(void* arg); static status_t _RefreshModelThreadWorker(void* arg);
static status_t _PopulatePackageWorker(void* arg); static status_t _PopulatePackageWorker(void* arg);
static status_t _PackagesToShowWorker(void* arg); static status_t _PackagesToShowWorker(void* arg);
@@ -148,6 +150,11 @@ private:
const UserDetail& userDetail); const UserDetail& userDetail);
void _HandleScreenshotCached(const BMessage* message); void _HandleScreenshotCached(const BMessage* message);
void _HandleIconsChanged();
void _SetStateForPackagesByName(
BStringList& packageNames,
PackageState state);
private: private:
FilterView* fFilterView; FilterView* fFilterView;
@@ -1,93 +0,0 @@
/*
* Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "MessagePackageListener.h"
#include <Messenger.h>
#include <View.h>
#include "PackageInfo.h"
// #pragma mark - MessagePackageListener
MessagePackageListener::MessagePackageListener(BHandler* target)
:
fTarget(target),
fChangesMask(0xffffffff)
{
}
MessagePackageListener::~MessagePackageListener()
{
}
void
MessagePackageListener::PackageChanged(const PackageInfoEvent& event)
{
if ((event.Changes() & fChangesMask) == 0)
return;
BMessenger messenger(fTarget);
if (!messenger.IsValid())
return;
BMessage message(MSG_UPDATE_PACKAGE);
message.AddString("name", event.Package()->Name());
message.AddUInt32("changes", event.Changes());
messenger.SendMessage(&message);
}
void
MessagePackageListener::SetChangesMask(uint32 mask)
{
fChangesMask = mask;
}
// #pragma mark - OnePackageMessagePackageListener
OnePackageMessagePackageListener::OnePackageMessagePackageListener(BHandler* target)
:
MessagePackageListener(target)
{
}
OnePackageMessagePackageListener::~OnePackageMessagePackageListener()
{
}
void
OnePackageMessagePackageListener::SetPackage(const PackageInfoRef& package)
{
if (fPackage == package)
return;
PackageInfoListenerRef listener(this);
if (fPackage.IsSet())
fPackage->RemoveListener(listener);
fPackage = package;
if (fPackage.IsSet())
fPackage->AddListener(listener);
}
const PackageInfoRef&
OnePackageMessagePackageListener::Package() const
{
return fPackage;
}

Some files were not shown because too many files have changed in this diff Show More