blob: 28f388dbb0f93904e957d307c2799e0e8fe5ca1f [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.jdbc.sql;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
import org.apache.openjpa.jdbc.kernel.JDBCStore;
import org.apache.openjpa.jdbc.meta.ClassMapping;
import org.apache.openjpa.jdbc.meta.FieldMapping;
import org.apache.openjpa.jdbc.schema.Column;
import org.apache.openjpa.jdbc.schema.ForeignKey;
import org.apache.openjpa.jdbc.schema.Table;
import org.apache.openjpa.kernel.exps.Context;
import org.apache.openjpa.kernel.exps.Value;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.util.InternalException;
import org.apache.openjpa.util.UnsupportedException;
import org.apache.openjpa.util.UserException;
/**
* A logical union made up of multiple distinct selects whose results are
* combined in memory.
*
* @author Abe White
*/
public class LogicalUnion
implements Union {
private static final Localizer _loc = Localizer.forPackage
(LogicalUnion.class);
protected final UnionSelect[] sels;
protected final DBDictionary dict;
protected final ClassMapping[] mappings;
protected final BitSet desc = new BitSet();
private boolean _distinct = true;
/**
* Constructor.
*
* @param conf system configuration
* @param sels the number of SQL selects to union together
*/
public LogicalUnion(JDBCConfiguration conf, int sels) {
this(conf, sels, null);
}
/**
* Constructor used to seed the internal selects.
*/
public LogicalUnion(JDBCConfiguration conf, Select[] seeds) {
this(conf, seeds.length, seeds);
}
/**
* Delegate constructor.
*/
protected LogicalUnion(JDBCConfiguration conf, int sels, Select[] seeds) {
if (sels == 0)
throw new InternalException("sels == 0");
dict = conf.getDBDictionaryInstance();
mappings = new ClassMapping[sels];
this.sels = new UnionSelect[sels];
SelectImpl seed;
for (int i = 0; i < sels; i++) {
seed = (seeds == null)
? (SelectImpl) conf.getSQLFactoryInstance().newSelect()
: (SelectImpl) seeds[i];
this.sels[i] = newUnionSelect(seed, i);
}
}
/**
* Create a new union select with the given delegate and union position.
*/
protected UnionSelect newUnionSelect(SelectImpl seed, int pos) {
return new UnionSelect(seed, pos);
}
@Override
public Select[] getSelects() {
return sels;
}
@Override
public boolean isUnion() {
return false;
}
@Override
public void abortUnion() {
}
@Override
public String getOrdering() {
return null;
}
@Override
public JDBCConfiguration getConfiguration() {
return sels[0].getConfiguration();
}
public DBDictionary getDBDictionary() {
return dict;
}
@Override
public SQLBuffer toSelect(boolean forUpdate, JDBCFetchConfiguration fetch) {
return dict.toSelect(sels[0], forUpdate, fetch);
}
@Override
public SQLBuffer getSQL() {
return sels.length == 1 ? sels[0].getSQL() : null;
}
@Override
public SQLBuffer toSelectCount() {
return dict.toSelectCount(sels[0]);
}
@Override
public boolean getAutoDistinct() {
return sels[0].getAutoDistinct();
}
@Override
public void setAutoDistinct(boolean distinct) {
for (UnionSelect sel : sels) {
sel.setAutoDistinct(distinct);
}
}
@Override
public boolean isDistinct() {
return _distinct;
}
@Override
public void setDistinct(boolean distinct) {
_distinct = distinct;
}
@Override
public boolean isLRS() {
return sels[0].isLRS();
}
@Override
public void setLRS(boolean lrs) {
for (UnionSelect sel : sels) {
sel.setLRS(lrs);
}
}
@Override
public int getExpectedResultCount() {
return sels[0].getExpectedResultCount();
}
@Override
public void setExpectedResultCount(int expectedResultCount,
boolean force) {
for (UnionSelect sel : sels) {
sel.setExpectedResultCount(expectedResultCount, force);
}
}
@Override
public int getJoinSyntax() {
return sels[0].getJoinSyntax();
}
@Override
public void setJoinSyntax(int syntax) {
for (UnionSelect sel : sels) {
sel.setJoinSyntax(syntax);
}
}
@Override
public boolean supportsRandomAccess(boolean forUpdate) {
if (sels.length == 1)
return sels[0].supportsRandomAccess(forUpdate);
return false;
}
@Override
public boolean supportsLocking() {
if (sels.length == 1)
return sels[0].supportsLocking();
for (UnionSelect sel : sels)
if (!sel.supportsLocking())
return false;
return true;
}
@Override
public boolean hasMultipleSelects() {
if (sels != null && sels.length > 1)
return true;
return sels[0].hasMultipleSelects();
}
@Override
public int getCount(JDBCStore store)
throws SQLException {
int count = 0;
for (UnionSelect sel : sels) {
count += sel.getCount(store);
}
return count;
}
@Override
public Result execute(JDBCStore store, JDBCFetchConfiguration fetch)
throws SQLException {
if (fetch == null)
fetch = store.getFetchConfiguration();
return execute(store, fetch, fetch.getReadLockLevel());
}
@Override
public Result execute(JDBCStore store, JDBCFetchConfiguration fetch,
int lockLevel)
throws SQLException {
if (fetch == null)
fetch = store.getFetchConfiguration();
if (sels.length == 1) {
Result res = sels[0].execute(store, fetch, lockLevel);
((AbstractResult) res).setBaseMapping(mappings[0]);
return res;
}
if (getExpectedResultCount() == 1) {
AbstractResult res;
for (int i = 0; i < sels.length; i++) {
res = (AbstractResult) sels[i].execute(store, fetch,
lockLevel);
res.setBaseMapping(mappings[i]);
res.setIndexOf(i);
// if we get to the last select, just return its result
if (i == sels.length - 1)
return res;
// return the first result that has a row
try {
if (!res.next())
res.close();
else {
res.pushBack();
return res;
}
}
catch (SQLException se) {
res.close();
throw se;
}
}
}
// create a single result from each select in our fake union, merging
// them as needed
AbstractResult[] res = new AbstractResult[sels.length];
List[] orderIdxs = null;
try {
List l;
for (int i = 0; i < res.length; i++) {
res[i] = (AbstractResult) sels[i].execute(store, fetch,
lockLevel);
res[i].setBaseMapping(mappings[i]);
res[i].setIndexOf(i);
l = sels[i].getSelectedOrderIndexes();
if (l != null) {
if (orderIdxs == null)
orderIdxs = new List[sels.length];
orderIdxs[i] = l;
}
}
} catch (SQLException se) {
for (int i = 0; res[i] != null; i++)
res[i].close();
throw se;
}
// if multiple selects have ordering, use a comparator to collate
ResultComparator comp = null;
if (orderIdxs != null)
comp = new ResultComparator(orderIdxs, desc, dict);
return new MergedResult(res, comp);
}
@Override
public void select(Union.Selector selector) {
for (int i = 0; i < sels.length; i++)
selector.select(sels[i], i);
}
@Override
public String toString() {
return toSelect(false, null).getSQL();
}
/**
* A callback used to create the selects in a SQL union.
*/
public interface Selector {
/**
* Populate the <code>i</code>th select in the union.
*/
void select(Select sel, int i);
}
/**
* A select that is part of a logical union.
*/
public class UnionSelect
implements Select {
protected final SelectImpl sel;
protected final int pos;
protected int orders = 0;
protected List orderIdxs = null;
public UnionSelect(SelectImpl sel, int pos) {
this.sel = sel;
this.pos = pos;
sel.setRecordOrderedIndexes(true);
}
/**
* Delegate select.
*/
public SelectImpl getDelegate() {
return sel;
}
/**
* Return the indexes of the data in the select clause this query is
* ordered by.
*/
public List getSelectedOrderIndexes() {
if (orderIdxs == null)
orderIdxs = sel.getOrderedIndexes();
return orderIdxs;
}
@Override
public JDBCConfiguration getConfiguration() {
return sel.getConfiguration();
}
@Override
public int indexOf() {
return pos;
}
@Override
public SQLBuffer toSelect(boolean forUpdate,
JDBCFetchConfiguration fetch) {
return sel.toSelect(forUpdate, fetch);
}
@Override
public SQLBuffer getSQL() {
return sel.getSQL();
}
@Override
public SQLBuffer toSelectCount() {
return sel.toSelectCount();
}
@Override
public boolean getAutoDistinct() {
return sel.getAutoDistinct();
}
@Override
public void setAutoDistinct(boolean distinct) {
sel.setAutoDistinct(distinct);
}
@Override
public boolean isDistinct() {
return sel.isDistinct();
}
@Override
public void setDistinct(boolean distinct) {
sel.setDistinct(distinct);
}
@Override
public boolean isLRS() {
return sel.isLRS();
}
@Override
public void setLRS(boolean lrs) {
sel.setLRS(lrs);
}
@Override
public int getJoinSyntax() {
return sel.getJoinSyntax();
}
@Override
public void setJoinSyntax(int joinSyntax) {
sel.setJoinSyntax(joinSyntax);
}
@Override
public boolean supportsRandomAccess(boolean forUpdate) {
return sel.supportsRandomAccess(forUpdate);
}
@Override
public boolean supportsLocking() {
return sel.supportsLocking();
}
@Override
public boolean hasMultipleSelects() {
return sel.hasMultipleSelects();
}
@Override
public int getCount(JDBCStore store)
throws SQLException {
return sel.getCount(store);
}
@Override
public Result execute(JDBCStore store, JDBCFetchConfiguration fetch)
throws SQLException {
return sel.execute(store, fetch);
}
@Override
public Result execute(JDBCStore store, JDBCFetchConfiguration fetch,
int lockLevel)
throws SQLException {
return sel.execute(store, fetch, lockLevel);
}
@Override
public List getSubselects() {
return Collections.EMPTY_LIST;
}
@Override
public Select getParent() {
return null;
}
@Override
public String getSubselectPath() {
return null;
}
@Override
public void setParent(Select parent, String path) {
throw new UnsupportedException(_loc.get("union-element"));
}
@Override
public void setHasSubselect(boolean hasSub) {
sel.setHasSubselect(hasSub);
}
@Override
public boolean getHasSubselect() {
return sel.getHasSubselect();
}
@Override
public Select getFromSelect() {
return null;
}
@Override
public void setFromSelect(Select sel) {
throw new UnsupportedException(_loc.get("union-element"));
}
@Override
public boolean hasEagerJoin(boolean toMany) {
return sel.hasEagerJoin(toMany);
}
@Override
public boolean hasJoin(boolean toMany) {
return sel.hasJoin(toMany);
}
@Override
public boolean isSelected(Table table) {
return sel.isSelected(table);
}
@Override
public Collection getTableAliases() {
return sel.getTableAliases();
}
@Override
public List getSelects() {
return sel.getSelects();
}
@Override
public List getSelectAliases() {
return sel.getSelectAliases();
}
@Override
public List getIdentifierAliases() {
return sel.getIdentifierAliases();
}
@Override
public SQLBuffer getOrdering() {
return sel.getOrdering();
}
@Override
public SQLBuffer getGrouping() {
return sel.getGrouping();
}
@Override
public SQLBuffer getWhere() {
return sel.getWhere();
}
@Override
public SQLBuffer getHaving() {
return sel.getHaving();
}
@Override
public void addJoinClassConditions() {
sel.addJoinClassConditions();
}
@Override
public Joins getJoins() {
return sel.getJoins();
}
@Override
public Iterator getJoinIterator() {
return sel.getJoinIterator();
}
@Override
public long getStartIndex() {
return sel.getStartIndex();
}
@Override
public long getEndIndex() {
return sel.getEndIndex();
}
@Override
public void setRange(long start, long end) {
sel.setRange(start, end);
}
@Override
public String getColumnAlias(Column col) {
return sel.getColumnAlias(col);
}
@Override
public String getColumnAlias(Column col, Joins joins) {
return sel.getColumnAlias(col, joins);
}
@Override
public String getColumnAlias(Column col, Object alias) {
return sel.getColumnAlias(col, alias);
}
@Override
public String getColumnAlias(String col, Table table) {
return sel.getColumnAlias(col, table);
}
@Override
public String getColumnAlias(String col, Table table, Joins joins) {
return sel.getColumnAlias(col, table, joins);
}
@Override
public boolean isAggregate() {
return sel.isAggregate();
}
@Override
public void setAggregate(boolean agg) {
sel.setAggregate(agg);
}
@Override
public boolean isLob() {
return sel.isLob();
}
@Override
public void setLob(boolean lob) {
sel.setLob(lob);
}
@Override
public void selectPlaceholder(String sql) {
sel.selectPlaceholder(sql);
}
@Override
public void clearSelects() {
sel.clearSelects();
}
@Override
public boolean select(SQLBuffer sql, Object id) {
return sel.select(sql, id);
}
@Override
public boolean select(SQLBuffer sql, Object id, Joins joins) {
return sel.select(sql, id, joins);
}
@Override
public boolean select(String sql, Object id) {
return sel.select(sql, id);
}
@Override
public boolean select(String sql, Object id, Joins joins) {
return sel.select(sql, id, joins);
}
@Override
public boolean select(Column col) {
return sel.select(col);
}
@Override
public boolean select(Column col, Joins joins) {
return sel.select(col, joins);
}
@Override
public int select(Column[] cols) {
return sel.select(cols);
}
@Override
public int select(Column[] cols, Joins joins) {
return sel.select(cols, joins);
}
@Override
public void select(ClassMapping mapping, int subclasses,
JDBCStore store, JDBCFetchConfiguration fetch, int eager) {
select(mapping, subclasses, store, fetch, eager, null, false);
}
@Override
public void select(ClassMapping mapping, int subclasses,
JDBCStore store, JDBCFetchConfiguration fetch, int eager,
Joins joins) {
select(mapping, subclasses, store, fetch, eager, joins, false);
}
private void select(ClassMapping mapping, int subclasses,
JDBCStore store, JDBCFetchConfiguration fetch, int eager,
Joins joins, boolean identifier) {
// if this is the first (primary) mapping selected for this
// SELECT, record it so we can figure out what the result type is
// since the discriminator might not be selected
if (mappings[pos] == null)
mappings[pos] = mapping;
sel.select(this, mapping, subclasses, store, fetch, eager,
joins, identifier);
}
@Override
public boolean selectIdentifier(Column col) {
return sel.selectIdentifier(col);
}
@Override
public boolean selectIdentifier(Column col, Joins joins) {
return sel.selectIdentifier(col, joins);
}
@Override
public int selectIdentifier(Column[] cols) {
return sel.selectIdentifier(cols);
}
@Override
public int selectIdentifier(Column[] cols, Joins joins) {
return sel.selectIdentifier(cols, joins);
}
@Override
public void selectIdentifier(ClassMapping mapping, int subclasses,
JDBCStore store, JDBCFetchConfiguration fetch, int eager) {
select(mapping, subclasses, store, fetch, eager, null, true);
}
@Override
public void selectIdentifier(ClassMapping mapping, int subclasses,
JDBCStore store, JDBCFetchConfiguration fetch, int eager,
Joins joins) {
select(mapping, subclasses, store, fetch, eager, joins, true);
}
@Override
public int selectPrimaryKey(ClassMapping mapping) {
return sel.selectPrimaryKey(mapping);
}
@Override
public int selectPrimaryKey(ClassMapping mapping, Joins joins) {
return sel.selectPrimaryKey(mapping, joins);
}
@Override
public int orderByPrimaryKey(ClassMapping mapping, boolean asc,
boolean select) {
return orderByPrimaryKey(mapping, asc, null, select);
}
@Override
public int orderByPrimaryKey(ClassMapping mapping, boolean asc,
Joins joins, boolean select) {
ClassMapping pks = mapping;
while (!pks.isPrimaryKeyObjectId(true))
pks = pks.getJoinablePCSuperclassMapping();
Column[] cols = pks.getPrimaryKeyColumns();
recordOrderColumns(cols, asc);
return sel.orderByPrimaryKey(mapping, asc, joins, select,
isUnion());
}
/**
* Record that we're ordering by a SQL expression.
*/
protected void recordOrder(Object ord, boolean asc) {
if (ord == null)
return;
orderIdxs = null;
int idx = orders++;
if (desc.get(idx) && asc)
throw new UserException(_loc.get("incompat-ordering"));
if (!asc)
desc.set(idx);
}
/**
* Record that we're ordering by the given columns.
*/
protected void recordOrderColumns(Column[] cols, boolean asc) {
for (Column col : cols) {
recordOrder(col, asc);
}
}
@Override
public boolean orderBy(Column col, boolean asc, boolean select) {
return orderBy(col, asc, null, select);
}
@Override
public boolean orderBy(Column col, boolean asc, Joins joins,
boolean select) {
recordOrder(col, asc);
return sel.orderBy(col, asc, joins, select, isUnion());
}
@Override
public int orderBy(Column[] cols, boolean asc, boolean select) {
return orderBy(cols, asc, null, select);
}
@Override
public int orderBy(Column[] cols, boolean asc, Joins joins,
boolean select) {
recordOrderColumns(cols, asc);
return sel.orderBy(cols, asc, joins, select, isUnion());
}
@Override
public boolean orderBy(SQLBuffer sql, boolean asc, boolean select,
Value selAs) {
return orderBy(sql, asc, null, select, selAs);
}
@Override
public boolean orderBy(SQLBuffer sql, boolean asc, Joins joins,
boolean select, Value selAs) {
recordOrder(sql.getSQL(false), asc);
return sel.orderBy(sql, asc, joins, select, isUnion(), selAs);
}
@Override
public boolean orderBy(String sql, boolean asc, boolean select) {
return orderBy(sql, asc, null, select);
}
@Override
public boolean orderBy(String sql, boolean asc, Joins joins,
boolean select) {
recordOrder(sql, asc);
return sel.orderBy(sql, asc, joins, select, isUnion());
}
@Override
public void clearOrdering() {
sel.clearOrdering();
}
@Override
public void wherePrimaryKey(Object oid, ClassMapping mapping,
JDBCStore store) {
sel.wherePrimaryKey(oid, mapping, store);
}
@Override
public void whereForeignKey(ForeignKey fk, Object oid,
ClassMapping mapping, JDBCStore store) {
sel.whereForeignKey(fk, oid, mapping, store);
}
@Override
public void where(Joins joins) {
sel.where(joins);
}
@Override
public void where(SQLBuffer sql) {
sel.where(sql);
}
@Override
public void where(SQLBuffer sql, Joins joins) {
sel.where(sql, joins);
}
@Override
public void where(String sql) {
sel.where(sql);
}
@Override
public void where(String sql, Joins joins) {
sel.where(sql, joins);
}
@Override
public void having(SQLBuffer sql) {
sel.having(sql);
}
@Override
public void having(SQLBuffer sql, Joins joins) {
sel.having(sql, joins);
}
@Override
public void having(String sql) {
sel.having(sql);
}
@Override
public void having(String sql, Joins joins) {
sel.having(sql, joins);
}
@Override
public void groupBy(SQLBuffer sql) {
sel.groupBy(sql);
}
@Override
public void groupBy(SQLBuffer sql, Joins joins) {
sel.groupBy(sql, joins);
}
@Override
public void groupBy(String sql) {
sel.groupBy(sql);
}
@Override
public void groupBy(String sql, Joins joins) {
sel.groupBy(sql, joins);
}
@Override
public void groupBy(Column col) {
sel.groupBy(col);
}
@Override
public void groupBy(Column col, Joins joins) {
sel.groupBy(col, joins);
}
@Override
public void groupBy(Column[] cols) {
sel.groupBy(cols);
}
@Override
public void groupBy(Column[] cols, Joins joins) {
sel.groupBy(cols, joins);
}
@Override
public void groupBy(ClassMapping mapping, int subclasses,
JDBCStore store, JDBCFetchConfiguration fetch) {
sel.groupBy(mapping, subclasses, store, fetch);
}
@Override
public void groupBy(ClassMapping mapping, int subclasses,
JDBCStore store, JDBCFetchConfiguration fetch, Joins joins) {
sel.groupBy(mapping, subclasses, store, fetch, joins);
}
@Override
public SelectExecutor whereClone(int sels) {
return sel.whereClone(sels);
}
@Override
public SelectExecutor fullClone(int sels) {
return sel.fullClone(sels);
}
@Override
public SelectExecutor eagerClone(FieldMapping key, int eagerType,
boolean toMany, int sels) {
SelectExecutor ex = sel.eagerClone(key, eagerType, toMany, sels);
return (ex == sel) ? this : ex;
}
@Override
public SelectExecutor getEager(FieldMapping key) {
SelectExecutor ex = sel.getEager(key);
return (ex == sel) ? this : ex;
}
@Override
public Joins newJoins() {
return sel.newJoins();
}
@Override
public Joins newOuterJoins() {
return sel.newOuterJoins();
}
@Override
public void append(SQLBuffer buf, Joins joins) {
sel.append(buf, joins);
}
@Override
public Joins and(Joins joins1, Joins joins2) {
return sel.and(joins1, joins2);
}
@Override
public Joins or(Joins joins1, Joins joins2) {
return sel.or(joins1, joins2);
}
@Override
public Joins outer(Joins joins) {
return sel.outer(joins);
}
@Override
public String toString() {
return sel.toString();
}
@Override
public int getExpectedResultCount() {
return sel.getExpectedResultCount();
}
@Override
public void setExpectedResultCount(int expectedResultCount,
boolean force) {
sel.setExpectedResultCount(expectedResultCount, force);
}
@Override
public void setContext(Context context) {
sel.setContext(context);
}
@Override
public Context ctx() {
return sel.ctx();
}
@Override
public void setSchemaAlias(String schemaAlias) {
sel.setSchemaAlias(schemaAlias);
}
@Override
public void logEagerRelations() {
sel.logEagerRelations();
}
@Override
public void setTablePerClassMeta(ClassMapping meta) {
}
@Override
public ClassMapping getTablePerClassMeta() {
return sel.getTablePerClassMeta();
}
@Override
public void setJoinedTableClassMeta(List meta) {
sel.setJoinedTableClassMeta(meta);
}
@Override
public List getJoinedTableClassMeta() {
return sel.getJoinedTableClassMeta();
}
@Override
public void setExcludedJoinedTableClassMeta(List meta) {
sel.setExcludedJoinedTableClassMeta(meta);
}
@Override
public List getExcludedJoinedTableClassMeta() {
return sel.getExcludedJoinedTableClassMeta();
}
@Override
public DBDictionary getDictionary() {
return dict;
}
}
/**
* Comparator for collating ordered results when faking a union.
*/
private static class ResultComparator
implements MergedResult.ResultComparator {
private final List[] _orders;
private final BitSet _desc;
private final DBDictionary _dict;
public ResultComparator(List[] orders, BitSet desc, DBDictionary dict) {
_orders = orders;
_desc = desc;
_dict = dict;
}
@Override
public Object getOrderingValue(Result res, int idx) {
// if one value just return it
ResultSet rs = ((ResultSetResult) res).getResultSet();
if (_orders[idx].size() == 1)
return getOrderingValue(rs, _orders[idx].get(0));
// return array of values
Object[] vals = new Object[_orders[idx].size()];
for (int i = 0; i < vals.length; i++)
vals[i] = getOrderingValue(rs, _orders[idx].get(i));
return vals;
}
/**
* Extract value at given index from result set.
*/
private Object getOrderingValue(ResultSet rs, Object i) {
try {
return _dict.getObject(rs, (Integer) i + 1, null);
} catch (SQLException se) {
throw SQLExceptions.getStore(se, _dict);
}
}
@Override
public int compare(Object o1, Object o2) {
if (o1 == o2)
return 0;
if (o1 == null)
return (_desc.get(0)) ? -1 : 1;
if (o2 == null)
return (_desc.get(0)) ? 1 : -1;
int cmp;
if (!(o1 instanceof Object[])) {
if (!(o2 instanceof Object[])) {
cmp = ((Comparable) o1).compareTo(o2);
return (_desc.get(0)) ? -cmp : cmp;
}
cmp = ((Comparable) o1).compareTo(((Object[]) o2)[0]);
if (cmp != 0)
return (_desc.get(0)) ? -cmp : cmp;
return -1;
}
if (!(o2 instanceof Object[])) {
cmp = ((Comparable) ((Object[]) o1)[0]).compareTo(o2);
if (cmp != 0)
return (_desc.get(0)) ? -cmp : cmp;
return 1;
}
Object[] a1 = (Object[]) o1;
Object[] a2 = (Object[]) o2;
for (int i = 0; i < a1.length; i++) {
cmp = ((Comparable) a1[i]).compareTo(a2[i]);
if (cmp != 0)
return (_desc.get(i)) ? -cmp : cmp;
}
return a1.length - a2.length;
}
}
}