use of com.biglybt.core.peer.PEPiece in project BiglyBT by BiglySoftware.
the class PieceGraphView method buildImage.
private void buildImage() {
if (canvas == null || canvas.isDisposed()) {
return;
}
// canvas.setBackground(ColorCache.getColor(canvas.getDisplay(), "#1b1b1b"));
Rectangle bounds = canvas.getClientArea();
if (bounds.isEmpty()) {
return;
}
if (dlm == null) {
canvas.redraw();
return;
}
PEPeerManager pm = dlm.getPeerManager();
DiskManager dm = dlm.getDiskManager();
if (pm == null || dm == null) {
canvas.redraw();
return;
}
final DiskManagerPiece[] dm_pieces = dm.getPieces();
if (dm_pieces == null || dm_pieces.length == 0) {
canvas.redraw();
return;
}
int numPieces = dm_pieces.length;
if (imgHaveAll == null || imgHaveAll.isDisposed()) {
imgHaveAll = new Image(canvas.getDisplay(), BLOCK_SIZE, BLOCK_SIZE);
GC gc = new GC(imgHaveAll);
try {
try {
gc.setAntialias(SWT.ON);
} catch (Exception e) {
// ignore
}
gc.setBackground(canvas.getBackground());
gc.fillRectangle(imgHaveAll.getBounds());
gc.setBackground(blockColors[BLOCKCOLOR_HAVEALL]);
gc.fillRoundRectangle(1, 1, BLOCK_FILLSIZE, BLOCK_FILLSIZE, BLOCK_FILLSIZE, BLOCK_FILLSIZE);
} finally {
gc.dispose();
}
}
if (imgNoHave == null || imgNoHave.isDisposed()) {
imgNoHave = new Image(canvas.getDisplay(), BLOCK_SIZE, BLOCK_SIZE);
GC gc = new GC(imgNoHave);
try {
try {
gc.setAntialias(SWT.ON);
} catch (Exception e) {
// ignore
}
gc.setBackground(canvas.getBackground());
gc.fillRectangle(imgNoHave.getBounds());
gc.setBackground(blockColors[BLOCKCOLOR_NOHAVE]);
gc.fillRoundRectangle(1, 1, BLOCK_FILLSIZE, BLOCK_FILLSIZE, BLOCK_FILLSIZE, BLOCK_FILLSIZE);
} finally {
gc.dispose();
}
}
boolean clearImage = img == null || img.isDisposed() || img.getBounds().width != bounds.width || img.getBounds().height != bounds.height;
if (clearImage) {
if (img != null && !img.isDisposed()) {
img.dispose();
}
// System.out.println("clear " + img);
img = new Image(canvas.getDisplay(), bounds.width, bounds.height);
squareCache = null;
}
PEPiece[] currentDLPieces = dlm.getCurrentPieces();
Arrays.sort(currentDLPieces, compFindPEPiece);
// find upload pieces
ArrayList currentULPieces = new ArrayList();
ArrayList futureULPieces = new ArrayList();
PEPeer[] peers = (PEPeer[]) pm.getPeers().toArray(new PEPeer[0]);
for (int i = 0; i < peers.length; i++) {
PEPeer peer = peers[i];
int[] peerRequestedPieces = peer.getIncomingRequestedPieceNumbers();
if (peerRequestedPieces != null && peerRequestedPieces.length > 0) {
currentULPieces.add(new Long(peerRequestedPieces[0]));
for (int j = 1; j < peerRequestedPieces.length; j++) {
futureULPieces.add(new Long(peerRequestedPieces[j]));
}
}
// we'll have duplicates
Collections.sort(currentULPieces);
Collections.sort(futureULPieces);
}
int iNumCols = bounds.width / BLOCK_SIZE;
int iNumRows = bounds.height / BLOCK_SIZE;
int numSquares = onePiecePerBlock ? numPieces : iNumCols * iNumRows;
double numPiecesPerSquare = numPieces / (double) numSquares;
if (squareCache == null || squareCache.length != numSquares) {
squareCache = new double[numSquares];
Arrays.fill(squareCache, -1);
}
int[] availability = pm.getAvailability();
int numRedraws = 0;
GC gc = new GC(img);
try {
int iRow = 0;
if (clearImage) {
gc.setBackground(canvas.getBackground());
gc.fillRectangle(bounds);
}
try {
gc.setAdvanced(true);
gc.setAntialias(SWT.ON);
gc.setInterpolation(SWT.HIGH);
} catch (Exception e) {
// ignore
}
int iCol = 0;
for (int squareNo = 0; squareNo < numSquares; squareNo++) {
if (iCol >= iNumCols) {
iCol = 0;
iRow++;
}
int startNo = (int) (squareNo * numPiecesPerSquare);
int count = (int) ((squareNo + 1) * numPiecesPerSquare) - startNo;
if (count == 0) {
count = 1;
}
// if (count > 1) System.out.println("!!! " + startNo);
// System.out.println(startNo + ";" + count);
double pctDone = getPercentDone(startNo, count, dm_pieces);
// System.out.print(pctDone + ";");
int colorIndex;
int iXPos = iCol * BLOCK_SIZE;
int iYPos = iRow * BLOCK_SIZE;
if (pctDone == 1) {
if (squareCache[squareNo] != pctDone) {
squareCache[squareNo] = pctDone;
gc.drawImage(imgHaveAll, iXPos, iYPos);
if (!clearImage) {
numRedraws++;
canvas.redraw(iXPos, iYPos, BLOCK_SIZE, BLOCK_SIZE, false);
}
}
} else if (pctDone == 0) {
if (squareCache[squareNo] != pctDone) {
squareCache[squareNo] = pctDone;
gc.drawImage(imgNoHave, iXPos, iYPos);
if (!clearImage) {
numRedraws++;
canvas.redraw(iXPos, iYPos, BLOCK_SIZE, BLOCK_SIZE, false);
}
}
} else {
// !done
boolean isDownloading = false;
for (int i = startNo; i < startNo + count; i++) {
if (Arrays.binarySearch(currentDLPieces, new Long(i), compFindPEPiece) >= 0) {
isDownloading = true;
break;
}
}
double val = pctDone + (isDownloading ? 0 : 1);
if (squareCache[squareNo] != val) {
squareCache[squareNo] = val;
gc.drawImage(imgNoHave, iXPos, iYPos);
int size = (int) (BLOCK_FILLSIZE * pctDone);
if (size == 0) {
size = 1;
}
int q = (int) ((BLOCK_FILLSIZE - size) / 2.0 + 0.5) + 1;
colorIndex = isDownloading ? BLOCKCOLOR_DOWNLOADING : BLOCKCOLOR_HAVESOME;
gc.setBackground(blockColors[colorIndex]);
gc.fillOval(iXPos + q, iYPos + q, size, size);
// gc.fillRoundRectangle(iXPos + q, iYPos + q, size, size, size, size);
if (!clearImage) {
numRedraws++;
canvas.redraw(iXPos, iYPos, BLOCK_SIZE, BLOCK_SIZE, false);
}
}
}
for (int i = startNo; i < startNo + count; i++) {
if (Collections.binarySearch(currentULPieces, new Long(i)) >= 0) {
colorIndex = BLOCKCOLOR_UPLOADING;
int size = BLOCK_FILLSIZE + 1;
gc.setForeground(blockColors[colorIndex]);
gc.drawRoundRectangle(iXPos, iYPos, size, size, size, size);
if (!clearImage) {
numRedraws++;
canvas.redraw(iXPos, iYPos, BLOCK_SIZE, BLOCK_SIZE, false);
}
squareCache[squareNo] = -1;
break;
} else if (Collections.binarySearch(futureULPieces, new Long(i)) >= 0) {
colorIndex = BLOCKCOLOR_UPLOADING;
int size = BLOCK_FILLSIZE + 1;
gc.setForeground(blockColors[colorIndex]);
gc.setLineStyle(SWT.LINE_DOT);
gc.drawRoundRectangle(iXPos, iYPos, size, size, size, size);
if (!clearImage) {
numRedraws++;
canvas.redraw(iXPos, iYPos, BLOCK_SIZE, BLOCK_SIZE, false);
}
gc.setLineStyle(SWT.LINE_SOLID);
squareCache[squareNo] = -1;
break;
}
}
if (availability != null) {
boolean hasNoAvail = false;
for (int i = startNo; i < startNo + count; i++) {
if (availability[i] == 0) {
hasNoAvail = true;
squareCache[squareNo] = -1;
break;
}
}
if (hasNoAvail) {
gc.setForeground(blockColors[BLOCKCOLOR_NOAVAIL]);
gc.drawRectangle(iXPos, iYPos, BLOCK_FILLSIZE + 1, BLOCK_FILLSIZE + 1);
if (!clearImage) {
numRedraws++;
canvas.redraw(iXPos, iYPos, BLOCK_SIZE, BLOCK_SIZE, false);
}
}
}
iCol++;
}
// System.out.println("redraws " + numRedraws);
} catch (Exception e) {
Debug.out(e);
} finally {
gc.dispose();
}
// canvas.redraw();
}
use of com.biglybt.core.peer.PEPiece in project BiglyBT by BiglySoftware.
the class PiecesView method initYourTableView.
// @see com.biglybt.ui.swt.views.table.impl.TableViewTab#initYourTableView()
@Override
public TableViewSWT<PEPiece> initYourTableView() {
tv = TableViewFactory.createTableViewSWT(PEPiece.class, TableManager.TABLE_TORRENT_PIECES, getPropertiesPrefix(), basicItems, basicItems[0].getName(), SWT.SINGLE | SWT.FULL_SELECTION | SWT.VIRTUAL);
tv.setEnableTabViews(enable_tabs, true, null);
UIFunctionsSWT uiFunctions = UIFunctionsManagerSWT.getUIFunctionsSWT();
if (uiFunctions != null) {
UISWTInstance pluginUI = uiFunctions.getUISWTInstance();
registerPluginViews(pluginUI);
}
tv.addTableDataSourceChangedListener(this, true);
tv.addMenuFillListener(this);
tv.addLifeCycleListener(this);
tv.addSelectionListener(this, false);
return tv;
}
use of com.biglybt.core.peer.PEPiece in project BiglyBT by BiglySoftware.
the class Show method printTorrentDetails.
/**
* prints out the full details of a particular torrent
* @param out
* @param dm
* @param torrentNum
*/
private static void printTorrentDetails(PrintStream out, DownloadManager dm, int torrentNum, List<String> args) {
String name = dm.getDisplayName();
if (name == null)
name = "?";
out.println("> -----");
out.println("Info on Torrent #" + torrentNum + " (" + name + ")");
out.println("- General Info -");
String[] health = { "- no info -", "stopped", "no remote connections", "no tracker", "OK", "ko" };
try {
out.println("Health: " + health[dm.getHealthStatus()]);
} catch (Exception e) {
out.println("Health: " + health[0]);
}
out.println("State: " + Integer.toString(dm.getState()));
if (dm.getState() == DownloadManager.STATE_ERROR)
out.println("Error: " + dm.getErrorDetails());
out.println("Hash: " + TorrentUtils.nicePrintTorrentHash(dm.getTorrent(), true));
out.println("- Torrent file -");
out.println("Torrent Filename: " + dm.getTorrentFileName());
out.println("Saving to: " + dm.getSaveLocation());
out.println("Created By: " + dm.getTorrentCreatedBy());
out.println("Comment: " + dm.getTorrentComment());
Category cat = dm.getDownloadState().getCategory();
if (cat != null) {
out.println("Category: " + cat.getName());
}
List<Tag> tags = TagManagerFactory.getTagManager().getTagsForTaggable(TagType.TT_DOWNLOAD_MANUAL, dm);
String tags_str;
if (tags.size() == 0) {
tags_str = "None";
} else {
tags_str = "";
for (Tag t : tags) {
tags_str += (tags_str.length() == 0 ? "" : ",") + t.getTagName(true);
}
}
out.println("Tags: " + tags_str);
out.println("- Tracker Info -");
TRTrackerAnnouncer trackerclient = dm.getTrackerClient();
if (trackerclient != null) {
out.println("URL: " + trackerclient.getTrackerURL());
String timestr;
try {
int time = trackerclient.getTimeUntilNextUpdate();
if (time < 0) {
timestr = MessageText.getString("GeneralView.label.updatein.querying");
} else {
int minutes = time / 60;
int seconds = time % 60;
String strSeconds = "" + seconds;
if (seconds < 10) {
// $NON-NLS-1$
strSeconds = "0" + seconds;
}
timestr = minutes + ":" + strSeconds;
}
} catch (Exception e) {
timestr = "unknown";
}
out.println("Time till next Update: " + timestr);
out.println("Status: " + trackerclient.getStatusString());
} else
out.println(" Not available");
out.println("- Files Info -");
DiskManagerFileInfo[] files = dm.getDiskManagerFileInfo();
if (files != null) {
for (int i = 0; i < files.length; i++) {
out.print(((i < 9) ? " " : " ") + Integer.toString(i + 1) + " (");
String tmp = ">";
if (files[i].getPriority() > 0)
tmp = "+";
if (files[i].isSkipped())
tmp = "!";
out.print(tmp + ") ");
if (files[i] != null) {
long fLen = files[i].getLength();
if (fLen > 0) {
DecimalFormat df = new DecimalFormat("000.0%");
out.print(df.format(files[i].getDownloaded() * 1.0 / fLen));
out.println("\t" + files[i].getFile(true).getName());
} else
out.println("Info not available.");
} else
out.println("Info not available.");
}
} else {
out.println(" Info not available.");
}
for (String arg : args) {
arg = arg.toLowerCase();
if (arg.startsWith("pie")) {
out.println("Pieces");
PEPeerManager pm = dm.getPeerManager();
if (pm != null) {
PiecePicker picker = pm.getPiecePicker();
PEPiece[] pieces = pm.getPieces();
String line = "";
for (int i = 0; i < pieces.length; i++) {
String str = picker.getPieceString(i);
line += (line.length() == 0 ? (i + " ") : ",") + str;
PEPiece piece = pieces[i];
if (piece != null) {
line += ":" + piece.getString();
}
if ((i + 1) % 10 == 0) {
out.println(line);
line = "";
}
}
if (line.length() > 0) {
out.println(line);
}
}
} else if (arg.startsWith("pee")) {
out.println("Peers");
PEPeerManager pm = dm.getPeerManager();
if (pm != null) {
List<PEPeer> peers = pm.getPeers();
out.println("\tConnected to " + peers.size() + " peers");
for (PEPeer peer : peers) {
PEPeerStats stats = peer.getStats();
System.out.println("\t\t" + peer.getIp() + ", in=" + (peer.isIncoming() ? "Y" : "N") + ", prot=" + peer.getProtocol() + ", choked=" + (peer.isChokingMe() ? "Y" : "N") + ", up=" + DisplayFormatters.formatByteCountToKiBEtcPerSec(stats.getDataSendRate() + stats.getProtocolSendRate()) + ", down=" + DisplayFormatters.formatByteCountToKiBEtcPerSec(stats.getDataReceiveRate() + stats.getProtocolReceiveRate()) + ", in_req=" + peer.getIncomingRequestCount() + ", out_req=" + peer.getOutgoingRequestCount());
}
}
}
}
out.println("> -----");
}
use of com.biglybt.core.peer.PEPiece in project BiglyBT by BiglySoftware.
the class FileInfoView method createFileInfoPanel.
private Composite createFileInfoPanel(Composite parent) {
GridLayout layout;
GridData gridData;
// Peer Info section contains
// - Peer's Block display
// - Peer's Datarate
fileInfoComposite = new Composite(parent, SWT.NONE);
layout = new GridLayout();
layout.numColumns = 2;
layout.horizontalSpacing = 0;
layout.verticalSpacing = 0;
layout.marginHeight = 0;
layout.marginWidth = 0;
fileInfoComposite.setLayout(layout);
gridData = new GridData(GridData.FILL, GridData.FILL, true, true);
fileInfoComposite.setLayoutData(gridData);
new Label(fileInfoComposite, SWT.NULL).setLayoutData(new GridData());
topLabel = new Label(fileInfoComposite, SWT.NULL);
gridData = new GridData(SWT.FILL, SWT.DEFAULT, false, false);
topLabel.setLayoutData(gridData);
sc = new ScrolledComposite(fileInfoComposite, SWT.V_SCROLL);
sc.setExpandHorizontal(true);
sc.setExpandVertical(true);
layout = new GridLayout();
layout.horizontalSpacing = 0;
layout.verticalSpacing = 0;
layout.marginHeight = 0;
layout.marginWidth = 0;
sc.setLayout(layout);
gridData = new GridData(GridData.FILL, GridData.FILL, true, true, 2, 1);
sc.setLayoutData(gridData);
fileInfoCanvas = new Canvas(sc, SWT.NO_REDRAW_RESIZE | SWT.NO_BACKGROUND);
gridData = new GridData(GridData.FILL, SWT.DEFAULT, true, false);
fileInfoCanvas.setLayoutData(gridData);
fileInfoCanvas.addPaintListener(new PaintListener() {
@Override
public void paintControl(PaintEvent e) {
if (e.width <= 0 || e.height <= 0)
return;
try {
Rectangle bounds = (img == null) ? null : img.getBounds();
if (bounds == null) {
e.gc.fillRectangle(e.x, e.y, e.width, e.height);
} else {
if (e.x + e.width > bounds.width)
e.gc.fillRectangle(bounds.width, e.y, e.x + e.width - bounds.width + 1, e.height);
if (e.y + e.height > bounds.height)
e.gc.fillRectangle(e.x, bounds.height, e.width, e.y + e.height - bounds.height + 1);
int width = Math.min(e.width, bounds.width - e.x);
int height = Math.min(e.height, bounds.height - e.y);
e.gc.drawImage(img, e.x, e.y, width, height, e.x, e.y, width, height);
}
} catch (Exception ex) {
}
}
});
fileInfoCanvas.addMouseTrackListener(new MouseTrackAdapter() {
@Override
public void mouseHover(MouseEvent event) {
showPieceDetails(event.x, event.y);
}
});
Listener doNothingListener = new Listener() {
@Override
public void handleEvent(Event event) {
}
};
fileInfoCanvas.addListener(SWT.KeyDown, doNothingListener);
final Menu menu = new Menu(fileInfoCanvas.getShell(), SWT.POP_UP);
fileInfoCanvas.setMenu(menu);
fileInfoCanvas.addListener(SWT.MenuDetect, new Listener() {
@Override
public void handleEvent(Event event) {
Point pt = fileInfoCanvas.toControl(event.x, event.y);
int piece_number = getPieceNumber(pt.x, pt.y);
menu.setData("pieceNumber", piece_number);
}
});
MenuBuildUtils.addMaintenanceListenerForMenu(menu, new MenuBuildUtils.MenuBuilder() {
@Override
public void buildMenu(Menu menu, MenuEvent event) {
Integer pn = (Integer) menu.getData("pieceNumber");
if (pn != null && pn != -1) {
DownloadManager download_manager = file.getDownloadManager();
if (download_manager == null) {
return;
}
DiskManager disk_manager = download_manager.getDiskManager();
PEPeerManager peer_manager = download_manager.getPeerManager();
if (disk_manager == null || peer_manager == null) {
return;
}
final PiecePicker picker = peer_manager.getPiecePicker();
DiskManagerPiece[] dm_pieces = disk_manager.getPieces();
PEPiece[] pe_pieces = peer_manager.getPieces();
final int piece_number = pn;
final DiskManagerPiece dm_piece = dm_pieces[piece_number];
final PEPiece pe_piece = pe_pieces[piece_number];
final MenuItem force_piece = new MenuItem(menu, SWT.CHECK);
Messages.setLanguageText(force_piece, "label.force.piece");
boolean done = dm_piece.isDone();
force_piece.setEnabled(!done);
if (!done) {
force_piece.setSelection(picker.isForcePiece(piece_number));
force_piece.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
picker.setForcePiece(piece_number, force_piece.getSelection());
}
});
}
final MenuItem reset_piece = new MenuItem(menu, SWT.PUSH);
Messages.setLanguageText(reset_piece, "label.reset.piece");
boolean can_reset = dm_piece.isDone() || dm_piece.getNbWritten() > 0;
reset_piece.setEnabled(can_reset);
reset_piece.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
dm_piece.reset();
if (pe_piece != null) {
pe_piece.reset();
}
}
});
}
}
});
fileInfoCanvas.addListener(SWT.Resize, new Listener() {
@Override
public void handleEvent(Event e) {
if (refreshInfoCanvasQueued) {
return;
}
refreshInfoCanvasQueued = true;
// wrap in asyncexec because sc.setMinWidth (called later) doesn't work
// too well inside a resize (the canvas won't size isn't always updated)
Utils.execSWTThreadLater(0, new AERunnable() {
@Override
public void runSupport() {
if (img != null) {
int iOldColCount = img.getBounds().width / BLOCK_SIZE;
int iNewColCount = fileInfoCanvas.getClientArea().width / BLOCK_SIZE;
if (iOldColCount != iNewColCount)
refreshInfoCanvas();
}
}
});
}
});
sc.setContent(fileInfoCanvas);
Legend.createLegendComposite(fileInfoComposite, blockColors, new String[] { "FileView.BlockView.Done", "FileView.BlockView.Skipped", "FileView.BlockView.Active", "FileView.BlockView.Outstanding" }, new GridData(SWT.FILL, SWT.DEFAULT, true, false, 2, 1));
int iFontPixelsHeight = 10;
int iFontPointHeight = (iFontPixelsHeight * 72) / Utils.getDPIRaw(fileInfoCanvas.getDisplay()).y;
Font f = fileInfoCanvas.getFont();
FontData[] fontData = f.getFontData();
fontData[0].setHeight(iFontPointHeight);
font = new Font(fileInfoCanvas.getDisplay(), fontData);
return fileInfoComposite;
}
use of com.biglybt.core.peer.PEPiece in project BiglyBT by BiglySoftware.
the class FileInfoView method refreshInfoCanvas.
protected void refreshInfoCanvas() {
if (fileInfoCanvas == null || fileInfoCanvas.isDisposed()) {
return;
}
refreshInfoCanvasQueued = false;
Rectangle bounds = fileInfoCanvas.getClientArea();
if (bounds.width <= 0 || bounds.height <= 0)
return;
if (img != null && !img.isDisposed()) {
img.dispose();
img = null;
}
DownloadManager download_manager = file == null ? null : file.getDownloadManager();
DiskManager disk_manager = download_manager == null ? null : download_manager.getDiskManager();
PEPeerManager peer_manager = download_manager == null ? null : download_manager.getPeerManager();
if (file == null || disk_manager == null || peer_manager == null) {
GC gc = new GC(fileInfoCanvas);
gc.fillRectangle(bounds);
gc.dispose();
return;
}
int first_piece = file.getFirstPieceNumber();
int num_pieces = file.getNbPieces();
int iNumCols = bounds.width / BLOCK_SIZE;
int iNeededHeight = (((num_pieces - 1) / iNumCols) + 1) * BLOCK_SIZE;
if (sc.getMinHeight() != iNeededHeight) {
sc.setMinHeight(iNeededHeight);
sc.layout(true, true);
bounds = fileInfoCanvas.getClientArea();
}
img = new Image(fileInfoCanvas.getDisplay(), bounds.width, iNeededHeight);
GC gcImg = new GC(img);
try {
gcImg.setBackground(fileInfoCanvas.getBackground());
gcImg.fillRectangle(0, 0, bounds.width, bounds.height);
DiskManagerPiece[] dm_pieces = disk_manager.getPieces();
PEPiece[] pe_pieces = peer_manager.getPieces();
int iRow = 0;
int iCol = 0;
for (int i = first_piece; i < first_piece + num_pieces; i++) {
DiskManagerPiece dm_piece = dm_pieces[i];
PEPiece pe_piece = pe_pieces[i];
int colorIndex;
int iXPos = iCol * BLOCK_SIZE;
int iYPos = iRow * BLOCK_SIZE;
if (dm_piece.isDone()) {
colorIndex = BLOCKCOLOR_DONE;
} else if (!dm_piece.isNeeded()) {
colorIndex = BLOCKCOLOR_SKIPPED;
} else if (pe_piece != null) {
colorIndex = BLOCKCOLOR_ACTIVE;
} else {
colorIndex = BLOCKCOLOR_NEEDED;
}
gcImg.setBackground(blockColors[colorIndex]);
gcImg.fillRectangle(iXPos, iYPos, BLOCK_FILLSIZE, BLOCK_FILLSIZE);
iCol++;
if (iCol >= iNumCols) {
iCol = 0;
iRow++;
}
}
} catch (Exception e) {
Logger.log(new LogEvent(LogIDs.GUI, "drawing piece map", e));
} finally {
gcImg.dispose();
}
fileInfoCanvas.redraw();
}
Aggregations