// This file is part of OpenTSDB.
// Copyright (C) 2015 The OpenTSDB Authors.
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 2.1 of the License, or (at your
// option) any later version. This program is distributed in the hope that it
// will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
// General Public License for more details. You should have received a copy
// of the GNU Lesser General Public License along with this program. If not,
// see <http://www.gnu.org/licenses/>.
package net.opentsdb.query.expression;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import org.hbase.async.Bytes.ByteMap;
import net.opentsdb.core.DataPoint;
import net.opentsdb.core.DataPoints;
import net.opentsdb.core.SeekableView;
import net.opentsdb.meta.Annotation;
import com.stumbleupon.async.Callback;
import com.stumbleupon.async.Deferred;
/**
* A class to store an array of data points processed through expressions along
* with the original meta data of the result set (metric, tags, etc).
* @since 2.3
*/
public class PostAggregatedDataPoints implements DataPoints {
/** The original results from storage, used for fetching meta data */
private final DataPoints base_data_points;
/** The results of the expression calculation */
private final DataPoint[] points;
/** An optional alias for the results */
private String alias = null;
/**
* Default ctor
* @param base_data_points The original results from storage for fetching meta
* @param points The results of the expression calculation
*/
public PostAggregatedDataPoints(final DataPoints base_data_points,
final DataPoint[] points) {
if (base_data_points == null) {
throw new IllegalArgumentException("base_data_points cannot be null");
}
if (points == null) {
throw new IllegalArgumentException("points cannot be null");
}
this.base_data_points = base_data_points;
this.points = points;
}
@Override
public String metricName() {
try {
return metricNameAsync().join();
} catch (Exception e) {
throw new RuntimeException("Unexpected exception waiting for "
+ "name resolution", e);
}
}
@Override
public Deferred<String> metricNameAsync() {
if (alias != null) {
if (alias.contains("@") && getTagUids().size() > 0) {
// need to resolve the tag UIDs for the templating feature
class TemplateFill implements Callback<Deferred<String>,
Map<String, String>> {
@Override
public Deferred<String> call(final Map<String, String> tags)
throws Exception {
for (final Entry<String, String> pair : tags.entrySet()) {
alias = alias.replace("@" + pair.getKey(), pair.getValue());
}
return Deferred.fromResult(alias);
}
}
return base_data_points.getTagsAsync()
.addCallbackDeferring(new TemplateFill());
}
return Deferred.fromResult(alias);
}
return base_data_points.metricNameAsync();
}
@Override
public byte[] metricUID() {
if (alias != null) {
return new byte[] { };
}
return base_data_points.metricUID();
}
@Override
public Map<String, String> getTags() {
if (alias != null) {
return Collections.<String, String>emptyMap();
} else {
return base_data_points.getTags();
}
}
@Override
public Deferred<Map<String, String>> getTagsAsync() {
if (alias != null) {
return Deferred.fromResult(Collections.<String, String>emptyMap());
}
return base_data_points.getTagsAsync();
}
@Override
public List<String> getAggregatedTags() {
if (alias != null) {
return Collections.<String>emptyList();
}
return base_data_points.getAggregatedTags();
}
@Override
public Deferred<List<String>> getAggregatedTagsAsync() {
if (alias != null) {
return Deferred.fromResult(Collections.<String>emptyList());
}
return base_data_points.getAggregatedTagsAsync();
}
@Override
public List<byte[]> getAggregatedTagUids() {
if (alias != null) {
return Collections.<byte[]>emptyList();
}
return base_data_points.getAggregatedTagUids();
}
@Override
public List<String> getTSUIDs() {
return base_data_points.getTSUIDs();
}
@Override
public List<Annotation> getAnnotations() {
return base_data_points.getAnnotations();
}
@Override
public ByteMap<byte[]> getTagUids() {
return base_data_points.getTagUids();
}
@Override
public int getQueryIndex() {
return base_data_points.getQueryIndex();
}
@Override
public int size() {
return points.length;
}
@Override
public int aggregatedSize() {
return points.length;
}
@Override
public SeekableView iterator() {
return new SeekableViewImpl(points);
}
@Override
public long timestamp(int i) {
return points[i].timestamp();
}
@Override
public boolean isInteger(int i) {
return points[i].isInteger();
}
@Override
public long longValue(int i) {
return points[i].longValue();
}
@Override
public double doubleValue(int i) {
return points[i].doubleValue();
}
/**
* An iterator working over the data points resulting from the expression
* calculation.
*/
static class SeekableViewImpl implements SeekableView {
private int pos = 0;
private final DataPoint[] dps;
SeekableViewImpl(final DataPoint[] dps) {
this.dps = dps;
}
@Override
public boolean hasNext() {
return pos < dps.length;
}
@Override
public DataPoint next() {
if (hasNext()) {
return dps[pos++];
} else {
throw new NoSuchElementException("no more elements");
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public void seek(long timestamp) {
for (int i = pos; i < dps.length; i++) {
if (dps[i].timestamp() >= timestamp) {
break;
} else {
pos++;
}
}
}
}
/** @param alias The alias to set for the time series. Used in place of
* the metric and nulls out all tags. */
public void setAlias(String alias) {
this.alias = alias;
}
}