/*******************************************************************************
* Copyright (c) 2009, 2014 Ericsson
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Francois Chouinard - Initial API and implementation
* Alexandre Montplaisir - Merge with TmfCoalescedDataRequest
*******************************************************************************/
package fr.inria.linuxtools.internal.tmf.core.request;
import java.util.ArrayList;
import java.util.List;
import fr.inria.linuxtools.internal.tmf.core.TmfCoreTracer;
import fr.inria.linuxtools.tmf.core.event.ITmfEvent;
import fr.inria.linuxtools.tmf.core.request.ITmfEventRequest;
import fr.inria.linuxtools.tmf.core.request.TmfEventRequest;
import fr.inria.linuxtools.tmf.core.timestamp.ITmfTimestamp;
import fr.inria.linuxtools.tmf.core.timestamp.TmfTimeRange;
/**
* The TMF coalesced event request
*
* @author Francois Chouinard
* @since 3.0
*/
public class TmfCoalescedEventRequest extends TmfEventRequest {
// ------------------------------------------------------------------------
// Attributes
// ------------------------------------------------------------------------
/** The list of coalesced requests */
private final List<ITmfEventRequest> fRequests = new ArrayList<>();
/**
* We do not use super.fRange, because in the case of coalesced requests,
* the global range can be modified as sub-request are added.
*/
private TmfTimeRange fRange;
// ------------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------------
/**
* Request 'n' events of a given type for the given time range (given
* priority). Events are returned in blocks of the given size.
*
* @param dataType
* The requested data type
* @param range
* The range of the request. You can use
* {@link TmfTimeRange#ETERNITY} to request all events.
* @param index
* The index of the first event to retrieve. Use '0' to start at
* the beginning.
* @param nbRequested
* The number of events requested. You can use
* {@link TmfEventRequest#ALL_DATA} to request all events.
* @param priority
* The requested execution priority
*/
public TmfCoalescedEventRequest(Class<? extends ITmfEvent> dataType,
TmfTimeRange range,
long index,
int nbRequested,
ExecutionType priority) {
super(ITmfEvent.class, null, index, nbRequested, priority);
fRange = range;
if (TmfCoreTracer.isRequestTraced()) {
String type = getClass().getName();
type = type.substring(type.lastIndexOf('.') + 1);
@SuppressWarnings("nls")
String message = "CREATED "
+ (getExecType() == ITmfEventRequest.ExecutionType.BACKGROUND ? "(BG)" : "(FG)")
+ " Type=" + type + " Index=" + getIndex() + " NbReq=" + getNbRequested()
+ " Range=" + getRange()
+ " DataType=" + getDataType().getSimpleName();
TmfCoreTracer.traceRequest(this, message);
}
}
@Override
public TmfTimeRange getRange() {
return fRange;
}
// ------------------------------------------------------------------------
// Management
// ------------------------------------------------------------------------
/**
* Add a request to this one.
*
* @param request
* The request to add
*/
public void addRequest(ITmfEventRequest request) {
fRequests.add(request);
merge(request);
}
/**
* Check if a request is compatible with the current coalesced one
*
* @param request
* The request to verify
* @return If the request is compatible, true or false
*/
public boolean isCompatible(ITmfEventRequest request) {
if (request.getExecType() == getExecType() &&
ranksOverlap(request) &&
timeRangesOverlap(request)) {
return true;
}
return false;
}
private boolean ranksOverlap(ITmfEventRequest request) {
long start = request.getIndex();
long end = start + request.getNbRequested();
// Return true if either the start or end index falls within
// the coalesced request boundaries
return (start <= (fIndex + fNbRequested + 1) && (end >= fIndex - 1));
}
private boolean timeRangesOverlap(ITmfEventRequest request) {
ITmfTimestamp startTime = request.getRange().getStartTime();
ITmfTimestamp endTime = request.getRange().getEndTime();
return (startTime.compareTo(endTime) <= 0) &&
(fRange.getStartTime().compareTo(fRange.getEndTime()) <= 0);
}
private void merge(ITmfEventRequest request) {
long start = request.getIndex();
long end = Math.min(start + request.getNbRequested(), ITmfEventRequest.ALL_DATA);
if (start < fIndex) {
if (fNbRequested != ITmfEventRequest.ALL_DATA) {
fNbRequested += (fIndex - start);
}
fIndex = start;
}
if ((request.getNbRequested() == ITmfEventRequest.ALL_DATA) ||
(fNbRequested == ITmfEventRequest.ALL_DATA)) {
fNbRequested = ITmfEventRequest.ALL_DATA;
} else {
fNbRequested = (int) Math.max(end - fIndex, fNbRequested);
}
ITmfTimestamp startTime = request.getRange().getStartTime();
ITmfTimestamp endTime = request.getRange().getEndTime();
if (!fRange.contains(startTime) && fRange.getStartTime().compareTo(startTime) > 0) {
fRange = new TmfTimeRange(startTime, fRange.getEndTime());
}
if (!fRange.contains(endTime) && fRange.getEndTime().compareTo(endTime) < 0) {
fRange = new TmfTimeRange(fRange.getStartTime(), endTime);
}
}
/**
* @return The list of IDs of the sub-requests
*/
@SuppressWarnings("nls")
public String getSubRequestIds() {
StringBuffer result = new StringBuffer("[");
for (int i = 0; i < fRequests.size(); i++) {
if (i != 0) {
result.append(", ");
}
result.append(fRequests.get(i).getRequestId());
}
result.append("]");
return result.toString();
}
// ------------------------------------------------------------------------
// ITmfEventRequest
// ------------------------------------------------------------------------
@Override
public void handleData(ITmfEvent data) {
super.handleData(data);
long index = getIndex() + getNbRead() - 1;
for (ITmfEventRequest request : fRequests) {
long start = request.getIndex();
if (!request.isCompleted() && index >= start && request.getNbRead() < request.getNbRequested()) {
ITmfTimestamp ts = data.getTimestamp();
if (request.getRange().contains(ts)) {
if (request.getDataType().isInstance(data)) {
request.handleData(data);
}
}
}
}
}
@Override
public void start() {
for (ITmfEventRequest request : fRequests) {
if (!request.isCompleted()) {
request.start();
}
}
super.start();
}
@Override
public void done() {
for (ITmfEventRequest request : fRequests) {
if (!request.isCompleted()) {
request.done();
}
}
super.done();
}
@Override
public void fail() {
for (ITmfEventRequest request : fRequests) {
request.fail();
}
super.fail();
}
@Override
public void cancel() {
for (ITmfEventRequest request : fRequests) {
if (!request.isCompleted()) {
request.cancel();
}
}
super.cancel();
}
@Override
public synchronized boolean isCompleted() {
// Firstly, check if coalescing request is completed
if (super.isCompleted()) {
return true;
}
// Secondly, check if all sub-requests are finished
if (fRequests.size() > 0) {
// If all sub requests are completed the coalesced request is
// treated as completed, too.
for (ITmfEventRequest request : fRequests) {
if (!request.isCompleted()) {
return false;
}
}
return true;
}
// Coalescing request is not finished if there are no sub-requests
return false;
}
@Override
public synchronized boolean isCancelled() {
// Firstly, check if coalescing request is canceled
if (super.isCancelled()) {
return true;
}
// Secondly, check if all sub-requests are canceled
if (fRequests.size() > 0) {
// If all sub requests are canceled the coalesced request is
// treated as completed, too.
for (ITmfEventRequest request : fRequests) {
if (!request.isCancelled()) {
return false;
}
}
return true;
}
// Coalescing request is not canceled if there are no sub-requests
return false;
}
// ------------------------------------------------------------------------
// Object
// ------------------------------------------------------------------------
@Override
// All requests have a unique id
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object other) {
if (other instanceof TmfCoalescedEventRequest) {
TmfCoalescedEventRequest request = (TmfCoalescedEventRequest) other;
return (request.getDataType() == getDataType()) &&
(request.getIndex() == getIndex()) &&
(request.getNbRequested() == getNbRequested()) &&
(request.getRange().equals(fRange));
}
return false;
}
@Override
@SuppressWarnings("nls")
public String toString() {
return "[TmfCoalescedEventRequest(" + getRequestId() + "," + getDataType().getSimpleName()
+ "," + getExecType() + "," + getRange() + "," + getIndex() + "," + getNbRequested()
+ ", " + fRequests.toString() + ")]";
}
}