blob: 61748bc22a656fcc138db97d6742dc165881b3e3 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openjpa.lib.rop;
import java.util.Comparator;
/**
* A result object provider that merges multiple result object provider
* delegates. Support exists for maintaining ordering of the internally held
* results, provided that each of the individual results is itself ordered.
*
* @author Abe White
* @author Marc Prud'hommeaux
*/
public class MergedResultObjectProvider implements ResultObjectProvider {
private static final byte UNOPENED = 0;
private static final byte OPENED = 1;
private static final byte VALUE = 2;
private static final byte DONE = 3;
private final ResultObjectProvider[] _rops;
private final Comparator _comp;
private final byte[] _status;
private Object[] _values;
private Object[] _orderValues;
private Object _cur = null;
private int _size = -1;
/**
* Constructor. Provide delegates.
*/
public MergedResultObjectProvider(ResultObjectProvider[] rops) {
this(rops, null);
}
/**
* Constructor. Provide delegates and optional comparator.
*/
public MergedResultObjectProvider(ResultObjectProvider[] rops,
Comparator comp) {
_rops = rops;
_comp = comp;
_status = new byte[rops.length];
_values = (comp == null) ? null : new Object[rops.length];
_orderValues = (comp == null) ? null : new Object[rops.length];
}
@Override
public boolean supportsRandomAccess() {
return false;
}
@Override
public void open() throws Exception {
// if we have a comparator, then open all; else open first
int len = (_comp != null) ? _rops.length : 1;
for (int i = 0; i < len; i++) {
_rops[i].open();
_status[i] = OPENED;
}
}
@Override
public boolean absolute(int pos) throws Exception {
throw new UnsupportedOperationException();
}
@Override
public int size() throws Exception {
if (_size != -1)
return _size;
// have to open all to get sizes
for (int i = 0; i < _status.length; i++) {
if (_status[i] == UNOPENED) {
_rops[i].open();
_status[i] = OPENED;
}
}
int total = 0;
int size;
for (ResultObjectProvider rop : _rops) {
size = rop.size();
if (size == Integer.MAX_VALUE) {
total = size;
break;
}
total += size;
}
_size = total;
return _size;
}
@Override
public void reset() throws Exception {
for (int i = 0; i < _rops.length; i++)
if (_status[i] != UNOPENED)
_rops[i].reset();
clear();
}
@Override
public void close() throws Exception {
Exception err = null;
for (int i = 0; i < _rops.length; i++) {
try {
if (_status[i] != UNOPENED)
_rops[i].close();
} catch (Exception e) {
if (err == null)
err = e;
}
}
clear();
if (err != null)
throw err;
}
private void clear() {
_cur = null;
for (int i = 0; i < _rops.length; i++) {
_status[i] = OPENED;
if (_values != null)
_values[i] = null;
if (_orderValues != null)
_orderValues[i] = null;
}
}
@Override
public void handleCheckedException(Exception e) {
if (_rops.length == 0)
throw new RuntimeException(e);
_rops[0].handleCheckedException(e);
}
@Override
public boolean next() throws Exception {
// initialize all rops with the latest values
boolean hasValue = false;
for (int i = 0; i < _status.length; i++) {
switch (_status[i]) {
case UNOPENED:
// this will only ever be the case if we aren't ordering
_rops[i].open();
_status[i] = OPENED;
// no break
case OPENED:
// if this rop has a value, cache it; if we're not ordering,
// then that's the value to return
if (_rops[i].next()) {
if (_comp == null) {
_cur = _rops[i].getResultObject();
return true;
} else {
hasValue = true;
_status[i] = VALUE;
_values[i] = _rops[i].getResultObject();
_orderValues[i] = getOrderingValue(_values[i],
i, _rops[i]);
}
} else
_status[i] = DONE;
break;
case VALUE:
// we only use this state when ordering
hasValue = true;
break;
}
}
// if we get to this point without a comparator, it means none
// of our rops have any more values
if (_comp == null || !hasValue)
return false;
// for all the rops with values, find the 'least' one according to
// the comparator
int least = -1;
Object orderVal = null;
for (int i = 0; i < _orderValues.length; i++) {
if (_status[i] != VALUE)
continue;
if (least == -1 || _comp.compare(_orderValues[i], orderVal) < 0) {
least = i;
orderVal = _orderValues[i];
}
}
// assign the least value to the current one, and clear the cached
// value for that rop so that we know to get the next value for
// the next comparison
_cur = _values[least];
_values[least] = null;
_orderValues[least] = null;
_status[least] = OPENED;
return true;
}
@Override
public Object getResultObject() throws Exception {
return _cur;
}
/**
* Return the value to use for ordering on the given result value. Returns
* the result value by default.
*
* @param val the result value
* @param idx the index of the result object provider in the array
* given on construction that produced the result value
* @param rop the result object provider that produced the result value
*/
protected Object getOrderingValue(Object val, int idx,
ResultObjectProvider rop) {
return val;
}
}