/*
* StreamCruncher: Copyright (c) 2006-2008, Ashwin Jayaprakash. All Rights Reserved.
* Contact: ashwin {dot} jayaprakash {at} gmail {dot} com
* Web: http://www.StreamCruncher.com
*
* This file is part of StreamCruncher.
*
* StreamCruncher 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 3 of the License, or
* (at your option) any later version.
*
* StreamCruncher 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 StreamCruncher. If not, see <http://www.gnu.org/licenses/>.
*/
package streamcruncher.innards.core.partition;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Set;
import streamcruncher.innards.core.QueryContext;
import streamcruncher.innards.core.partition.function.Function;
import streamcruncher.innards.core.partition.function.FunctionBuilder;
import streamcruncher.util.PerpetualResultSet;
/*
* Author: Ashwin Jayaprakash Date: Feb 18, 2006 Time: 8:53:32 AM
*/
public class PartitionDescender {
protected final FirstPartitionLevel firstPartitionLevel;
protected final Set<Function> dirtyFunctions;
protected final Set<Function> unprocessedFunctions;
protected final PriorityQueue<CalculateTSFunctionPair> calculateTSFunctionPairs;
protected final Object[] levelValueHolder;
public PartitionDescender(FirstPartitionLevel firstPartitionLevel,
Set<Function> dirtyFunctions, Set<Function> unprocessedFunctions,
PriorityQueue<CalculateTSFunctionPair> calculateTSFunctionPairs) {
this.firstPartitionLevel = firstPartitionLevel;
this.dirtyFunctions = dirtyFunctions;
this.unprocessedFunctions = unprocessedFunctions;
this.calculateTSFunctionPairs = calculateTSFunctionPairs;
// ---------
int levelCount = 0;
PartitionLevel level = firstPartitionLevel;
while (level != null) {
if (level.isDummyLevel() == false) {
levelCount++;
}
level = level.getNextLevel();
}
this.levelValueHolder = new Object[levelCount];
}
/**
* Constructs the whole Tree starting from the {@link FirstPartitionLevel}.
*
* @param context
* @param resultSet
* @return Rows in the ResultSet
* @throws Exception
*/
@SuppressWarnings("unchecked")
public int descendAndAddRows(QueryContext context, PerpetualResultSet resultSet)
throws Exception {
int counter = 0;
while (resultSet.next()) {
Map map = firstPartitionLevel.getColumnValueAndData();
PartitionLevel level = firstPartitionLevel;
Function function = null;
/*
* Descend to the last Level for the current Row and get the
* Function.
*/
int levelNum = 0;
for (;;) {
Object columnValue = null;
if (level.isDummyLevel() == false) {
columnValue = resultSet.getColumnValue(level.getColumnName());
levelValueHolder[levelNum++] = columnValue;
}
else {
// Use null as the key.
}
Object data = map.get(columnValue);
boolean weakRefNeedsRefresh = (level.isLastLevel() && data != null);
if (weakRefNeedsRefresh) {
WeakReference<Function> ref = (WeakReference<Function>) data;
/*
* WeakRef wrapped Object has been reclaimed, or the
* TableParitioner released the StrongRef, but the GC hasn't
* reclaimed yet.
*/
if (ref.get() == null || ref.get().canDiscard()) {
weakRefNeedsRefresh = true;
ref.clear();
}
else {
weakRefNeedsRefresh = false;
}
}
if (data == null || weakRefNeedsRefresh) {
if (level.isLastLevel()) {
FunctionBuilder builder = level.getFunctionBuilder();
Function tmpFn = builder.build(levelValueHolder);
// First, add a Strong-Reference.
firstPartitionLevel.addFunction(tmpFn.getHomeFunction());
tmpFn.setDirtyFunctions(dirtyFunctions);
tmpFn.setUnprocessedDataFunctions(unprocessedFunctions);
tmpFn.setCalculateTSFunctionPairs(calculateTSFunctionPairs);
tmpFn.cycleStart(context);
data = new WeakReference<Function>(tmpFn.getHomeFunction());
}
else {
data = new HashMap();
}
map.put(columnValue, data);
}
// -----------------
if (level.isLastLevel()) {
WeakReference<Function> ref = (WeakReference<Function>) data;
function = ref.get();
break;
}
level = level.getNextLevel();
map = (HashMap) data;
}
function.handleRow(context, resultSet);
counter++;
}
return counter;
}
public void attemptCleanup() {
Map map = firstPartitionLevel.getColumnValueAndData();
cleanMap(map);
}
@SuppressWarnings("unchecked")
protected void cleanMap(Map map) {
for (Iterator iter = map.keySet().iterator(); iter.hasNext();) {
Object key = iter.next();
Object value = map.get(key);
if (value == null) {
iter.remove();
continue;
}
if (value instanceof Map) {
Map innerMap = (Map) value;
cleanMap(innerMap);
if (innerMap.size() == 0) {
iter.remove();
}
}
else {
WeakReference<Function> ref = (WeakReference<Function>) value;
Function function = ref.get();
if (firstPartitionLevel.containsFunction(function) == false) {
iter.remove();
}
}
}
}
}