/** * Copyright (C) 2010-14 diirt developers. See COPYRIGHT.TXT * All rights reserved. Use is subject to license terms. See LICENSE.TXT */ package org.diirt.datasource.timecache.query; import java.io.PrintStream; import java.time.Instant; import java.util.Collections; import java.util.Iterator; import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; import org.diirt.datasource.timecache.Data; import org.diirt.datasource.timecache.util.CacheHelper; import org.diirt.util.time.TimeInterval; import org.diirt.vtype.VType; /** * Represents a chunk of {@link Data} with all {@link Instant} within a fixed * {@link TimeInterval}. * @author Fred Arnaud (Sopra Group) - ITER */ public class QueryChunk { // TODO: remove in final version... private static final boolean DEBUG = true; private static PrintStream ps = CacheHelper.ps; private final Query query; // ...until here and all debug blocks public static int Added = 1; public static int Updated = 0; public static int Ignored = -1; private static enum Status { NoDataReceived, SomeDataReceived, AllDataReceived, Blank } private final TimeInterval timeInterval; private SortedSet<Data> dataSet = Collections .synchronizedSortedSet(new TreeSet<Data>()); private Status status = Status.NoDataReceived; private boolean sent = false; public QueryChunk(TimeInterval timeInterval, Query query) { this.query = query; this.timeInterval = timeInterval; } public TimeInterval getTimeInterval() { return timeInterval; } public int addData(Data data, boolean forceUpdate) { if (data == null || (isComplete() && !forceUpdate) || !timeInterval.contains(data.getTimestamp())) return Ignored; if (isComplete() && DEBUG) { ps.println(query + ": " + this + ": REOPENED CHUNK with " + CacheHelper.format(data.getTimestamp())); } status = Status.SomeDataReceived; if (dataSet.add(data)) return Added; else return Updated; } public void markComplete() { if (this.dataSet.isEmpty()) this.status = Status.Blank; else this.status = Status.AllDataReceived; } public void invalidate() { if (this.dataSet.isEmpty()) this.status = Status.NoDataReceived; else this.status = Status.SomeDataReceived; } public void markSent() { this.sent = true; } public boolean hasBeenSent() { return sent; } public boolean isComplete() { return status.equals(Status.AllDataReceived) || status.equals(Status.Blank); } public void clearDataAndStatus() { this.dataSet.clear(); this.status = Status.NoDataReceived; } /** * Transform the chunk to the corresponding {@link QueryData} * implementation. */ public QueryData toQueryData() { switch (this.status) { case NoDataReceived: case SomeDataReceived: // We return only completed chunk return new QueryDataNR(timeInterval); case AllDataReceived: SortedMap<Instant, VType> sortedMap = new TreeMap<Instant, VType>(); Iterator<Data> itData = dataSet.iterator(); while (itData.hasNext()) { Data data = itData.next(); // TODO null is a specific case => update status ? if (data.getValue() != null) sortedMap.put(data.getTimestamp(), data.getValue()); } if (DEBUG) { ps.println(query + ": " + this + ": READ CHUNK"); } return new QueryDataComplete(timeInterval, sortedMap); case Blank: return new QueryDataBlank(timeInterval); default: return null; } } // Useful for debug public int getDataCount() { return dataSet.size(); } @Override public String toString() { return "QueryChunk (" + CacheHelper.format(timeInterval) + " " + dataSet.size() + " values, status: " + status + ")"; } }