1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.openjpa.lib.rop;
20
21 import java.util.Comparator;
22
23 import org.apache.commons.lang.exception.NestableRuntimeException;
24
25 /**
26 * A result object provider that merges multiple result object provider
27 * delegates. Support exists for maintaining ordering of the internally held
28 * results, provided that each of the individual results is itself ordered.
29 *
30 * @author Abe White
31 * @author Marc Prud'hommeaux
32 */
33 public class MergedResultObjectProvider implements ResultObjectProvider {
34
35 private static final byte UNOPENED = 0;
36 private static final byte OPENED = 1;
37 private static final byte VALUE = 2;
38 private static final byte DONE = 3;
39
40 private final ResultObjectProvider[] _rops;
41 private final Comparator _comp;
42 private final byte[] _status;
43 private Object[] _values;
44 private Object[] _orderValues;
45 private Object _cur = null;
46 private int _size = -1;
47
48 /**
49 * Constructor. Provide delegates.
50 */
51 public MergedResultObjectProvider(ResultObjectProvider[] rops) {
52 this(rops, null);
53 }
54
55 /**
56 * Constructor. Provide delegates and optional comparator.
57 */
58 public MergedResultObjectProvider(ResultObjectProvider[] rops,
59 Comparator comp) {
60 _rops = rops;
61 _comp = comp;
62 _status = new byte[rops.length];
63 _values = (comp == null) ? null : new Object[rops.length];
64 _orderValues = (comp == null) ? null : new Object[rops.length];
65 }
66
67 public boolean supportsRandomAccess() {
68 return false;
69 }
70
71 public void open() throws Exception {
72 // if we have a comparator, then open all; else open first
73 int len = (_comp != null) ? _rops.length : 1;
74 for (int i = 0; i < len; i++) {
75 _rops[i].open();
76 _status[i] = OPENED;
77 }
78 }
79
80 public boolean absolute(int pos) throws Exception {
81 throw new UnsupportedOperationException();
82 }
83
84 public int size() throws Exception {
85 if (_size != -1)
86 return _size;
87
88 // have to open all to get sizes
89 for (int i = 0; i < _status.length; i++) {
90 if (_status[i] == UNOPENED) {
91 _rops[i].open();
92 _status[i] = OPENED;
93 }
94 }
95
96 int total = 0;
97 int size;
98 for (int i = 0; i < _rops.length; i++) {
99 size = _rops[i].size();
100 if (size == Integer.MAX_VALUE) {
101 total = size;
102 break;
103 }
104 total += size;
105 }
106 _size = total;
107 return _size;
108 }
109
110 public void reset() throws Exception {
111 for (int i = 0; i < _rops.length; i++)
112 if (_status[i] != UNOPENED)
113 _rops[i].reset();
114 clear();
115 }
116
117 public void close() throws Exception {
118 Exception err = null;
119 for (int i = 0; i < _rops.length; i++) {
120 try {
121 if (_status[i] != UNOPENED)
122 _rops[i].close();
123 } catch (Exception e) {
124 if (err == null)
125 err = e;
126 }
127 }
128
129 clear();
130 if (err != null)
131 throw err;
132 }
133
134 private void clear() {
135 _cur = null;
136 for (int i = 0; i < _rops.length; i++) {
137 _status[i] = OPENED;
138 if (_values != null)
139 _values[i] = null;
140 if (_orderValues != null)
141 _orderValues[i] = null;
142 }
143 }
144
145 public void handleCheckedException(Exception e) {
146 if (_rops.length == 0)
147 throw new NestableRuntimeException(e);
148 _rops[0].handleCheckedException(e);
149 }
150
151 public boolean next() throws Exception {
152 // initialize all rops with the latest values
153 boolean hasValue = false;
154 for (int i = 0; i < _status.length; i++) {
155 switch (_status[i]) {
156 case UNOPENED:
157 // this will only ever be the case if we aren't ordering
158 _rops[i].open();
159 _status[i] = OPENED;
160 // no break
161 case OPENED:
162 // if this rop has a value, cache it; if we're not ordering,
163 // then that's the value to return
164 if (_rops[i].next()) {
165 if (_comp == null) {
166 _cur = _rops[i].getResultObject();
167 return true;
168 } else {
169 hasValue = true;
170 _status[i] = VALUE;
171 _values[i] = _rops[i].getResultObject();
172 _orderValues[i] = getOrderingValue(_values[i],
173 i, _rops[i]);
174 }
175 } else
176 _status[i] = DONE;
177 break;
178 case VALUE:
179 // we only use this state when ordering
180 hasValue = true;
181 break;
182 }
183 }
184
185 // if we get to this point without a comparator, it means none
186 // of our rops have any more values
187 if (_comp == null || !hasValue)
188 return false;
189
190 // for all the rops with values, find the 'least' one according to
191 // the comparator
192 int least = -1;
193 Object orderVal = null;
194 for (int i = 0; i < _orderValues.length; i++) {
195 if (_status[i] != VALUE)
196 continue;
197 if (least == -1 || _comp.compare(_orderValues[i], orderVal) < 0) {
198 least = i;
199 orderVal = _orderValues[i];
200 }
201 }
202
203 // assign the least value to the current one, and clear the cached
204 // value for that rop so that we know to get the next value for
205 // the next comparison
206 _cur = _values[least];
207 _values[least] = null;
208 _orderValues[least] = null;
209 _status[least] = OPENED;
210 return true;
211 }
212
213 public Object getResultObject() throws Exception {
214 return _cur;
215 }
216
217 /**
218 * Return the value to use for ordering on the given result value. Returns
219 * the result value by default.
220 *
221 * @param val the result value
222 * @param idx the index of the result object provider in the array
223 * given on construction that produced the result value
224 * @param rop the result object provider that produced the result value
225 */
226 protected Object getOrderingValue(Object val, int idx,
227 ResultObjectProvider rop) {
228 return val;
229 }
230 }