Java tutorial
/******************************************************************************* * Copyright 2012 Danny Kunz * * Licensed 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.omnaest.utils.table.impl.join; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Pattern; import org.apache.commons.lang3.ObjectUtils; import org.omnaest.utils.assertion.Assert; import org.omnaest.utils.operation.foreach.Range; import org.omnaest.utils.structure.array.ArrayUtils; import org.omnaest.utils.structure.collection.CollectionUtils; import org.omnaest.utils.structure.collection.list.ListUtils; import org.omnaest.utils.structure.collection.set.SetUtils; import org.omnaest.utils.structure.element.ElementHolder; import org.omnaest.utils.structure.element.converter.ElementConverter; import org.omnaest.utils.structure.element.converter.ElementConverterIdentity; import org.omnaest.utils.structure.element.factory.FactoryParameterized; import org.omnaest.utils.structure.element.factory.concrete.LinkedHashSetFactory; import org.omnaest.utils.structure.iterator.IterableUtils; import org.omnaest.utils.structure.map.MapUtils; import org.omnaest.utils.table.ImmutableColumn; import org.omnaest.utils.table.ImmutableColumn.ColumnIdentity; import org.omnaest.utils.table.ImmutableRow; import org.omnaest.utils.table.ImmutableTable; import org.omnaest.utils.table.Row; import org.omnaest.utils.table.Table; import org.omnaest.utils.table.TableExecution; import org.omnaest.utils.table.TableSelect; import org.omnaest.utils.table.TableSelect.Predicate.FilterRow; import org.omnaest.utils.table.TableSelect.TableJoin; import org.omnaest.utils.table.TableSelect.TableSelectExecution; import org.omnaest.utils.table.impl.ArrayTable; import com.google.common.base.Joiner; /** * @see TableSelect * @author Omnaest * @param <E> */ public class TableSelectImpl<E> implements TableSelect<E>, TableJoin<E>, TableSelectExecution<E> { /* ************************************** Variables / State (internal/hiding) ************************************* */ private List<Predicate<E>> predicateList = new ArrayList<Predicate<E>>(); private List<ColumnJoin<E>> columnJoinList = new ArrayList<TableSelectImpl.ColumnJoin<E>>(); private List<Bucket<E>> closedBucketList = new ArrayList<Bucket<E>>(); private Bucket<E> bucket; private Set<ImmutableTable<E>> tableForLockingSet = new LinkedHashSet<ImmutableTable<E>>(); private int top = -1; private int skip = 0; /* ********************************************** Classes/Interfaces ********************************************** */ private static final class PredicateEqualValue<E> implements Predicate<E> { private final E value; private final ColumnIdentity<E> columnIdentity; private PredicateEqualValue(E value, ColumnIdentity<E> columnIdentity) { this.value = value; this.columnIdentity = columnIdentity; } @Override public boolean isIncluding(TableSelect.Predicate.FilterRow<E> row) { final E element = row.getElement(this.columnIdentity); return ObjectUtils.equals(this.value, element); } } private static final class PredicateWithin<E> implements Predicate<E> { private final ColumnIdentity<E> columnIdentity; private final Set<E> valueSet; private PredicateWithin(ColumnIdentity<E> columnIdentity, Set<E> valueSet) { this.columnIdentity = columnIdentity; this.valueSet = valueSet; } @Override public boolean isIncluding(TableSelect.Predicate.FilterRow<E> row) { final E element = row.getElement(this.columnIdentity); return this.valueSet != null && this.valueSet.contains(element); } } private static final class PredicateLike<E> implements Predicate<E> { private final ColumnIdentity<E> columnIdentity; private final Pattern pattern; private PredicateLike(ColumnIdentity<E> columnIdentity, Pattern pattern) { this.columnIdentity = columnIdentity; this.pattern = pattern; } @Override public boolean isIncluding(TableSelect.Predicate.FilterRow<E> row) { final E element = row.getElement(this.columnIdentity); final String value = element != null ? String.valueOf(element) : null; final boolean retval = this.pattern != null && value != null && this.pattern.matcher(value).matches(); return retval; } } private static class MatchElement<E> { private final E element; private final boolean hasElement; private final boolean hasMatchingColumn; public MatchElement(E element, boolean hasElement, boolean hasMatchingColumn) { super(); this.element = element; this.hasElement = hasElement; this.hasMatchingColumn = hasMatchingColumn; } public boolean hasElement() { return this.hasElement; } public E getElement() { return this.element; } public boolean hasMatchingColumn() { return this.hasMatchingColumn; } } private static class IndexElementCalculator<E> { /* ************************************** Variables / State (internal/hiding) ************************************* */ private final ColumnJoin<E> columnJoin; /* *************************************************** Methods **************************************************** */ public IndexElementCalculator(ColumnJoin<E> columnJoin) { super(); this.columnJoin = columnJoin; } public MatchElement<E> calculate(FilterRow<E> filterRow) { MatchElement<E> retval = null; final Set<E> elementSet = new HashSet<E>(); if (filterRow != null) { final Set<ColumnIdentity<E>> columnIdentitySet = this.columnJoin.getColumnIdentitySet(); for (ColumnIdentity<E> columnIdentity : columnIdentitySet) { if (filterRow.hasColumn(columnIdentity)) { final E element = filterRow.getElement(columnIdentity); elementSet.add(element); } } } if (elementSet.size() == 1) { final E element = IterableUtils.firstElement(elementSet); final boolean hasElement = true; final boolean hasMatchingColumn = true; retval = new MatchElement<E>(element, hasElement, hasMatchingColumn); } else if (elementSet.size() == 0) { final boolean hasMatchingColumn = false; final boolean hasElement = false; final E element = null; retval = new MatchElement<E>(element, hasElement, hasMatchingColumn); } else { final boolean hasMatchingColumn = true; final boolean hasElement = false; final E element = null; retval = new MatchElement<E>(element, hasElement, hasMatchingColumn); } return retval; } } private static class ColumnJoinIndex<E> { private final ColumnJoin<E> columnJoin; private final Map<E, Set<FilterRow<E>>> elementToFilterRowSetMap = new HashMap<E, Set<FilterRow<E>>>(); private final IndexElementCalculator<E> indexElementCalculator; private final Set<FilterRow<E>> filterRowSet; private final boolean noIndexAvailable; public ColumnJoinIndex(ColumnJoin<E> columnJoin, Set<FilterRow<E>> filterRowSet) { super(); this.columnJoin = columnJoin; this.filterRowSet = filterRowSet; this.indexElementCalculator = new IndexElementCalculator<E>(this.columnJoin); this.noIndexAvailable = this.prepareIndex(filterRowSet); } private boolean prepareIndex(Iterable<FilterRow<E>> filterRowIterable) { final Map<E, Set<FilterRow<E>>> elementToFilterRowSetMapInitialized = MapUtils .initializedMap(this.elementToFilterRowSetMap, new LinkedHashSetFactory<FilterRow<E>>()); for (FilterRow<E> filterRow : filterRowIterable) { final MatchElement<E> matchElement = this.indexElementCalculator.calculate(filterRow); final boolean hasMatchingColumn = matchElement.hasMatchingColumn(); if (!hasMatchingColumn) { return true; } final boolean hasElement = matchElement.hasElement(); if (hasElement) { final E element = matchElement.getElement(); elementToFilterRowSetMapInitialized.get(element).add(filterRow); } } return false; } public Set<FilterRow<E>> indexFilteredFilterRowSet(FilterRow<E> filterRow) { Set<FilterRow<E>> retval = null; if (this.noIndexAvailable) { retval = this.filterRowSet; } else { final MatchElement<E> matchElement = this.indexElementCalculator.calculate(filterRow); if (!matchElement.hasMatchingColumn()) { retval = this.filterRowSet; } else { if (!matchElement.hasElement()) { retval = SetUtils.emptySet(); } else { final Set<FilterRow<E>> filterRowSet = this.elementToFilterRowSetMap .get(matchElement.getElement()); retval = filterRowSet; } } } return retval; } } private static class IndexBasedFilterRowIterableFactory<E> implements FactoryParameterized<Iterable<FilterRow<E>>, FilterRow<E>> { private final List<ColumnJoinIndex<E>> columnJoinIndexList = new ArrayList<ColumnJoinIndex<E>>(); private final Set<FilterRow<E>> filterRowSet; public IndexBasedFilterRowIterableFactory(Iterable<ColumnJoin<E>> columnJoinIterable, Iterable<FilterRow<E>> filterRowIterable) { super(); this.filterRowSet = SetUtils.valueOf(filterRowIterable); for (ColumnJoin<E> columnJoin : columnJoinIterable) { this.columnJoinIndexList.add(new ColumnJoinIndex<E>(columnJoin, this.filterRowSet)); } } @Override public Iterable<FilterRow<E>> newInstance(final FilterRow<E> filterRow) { if (!this.columnJoinIndexList.isEmpty()) { final List<Set<FilterRow<E>>> filterRowSetList = ListUtils.convert(this.columnJoinIndexList, new ElementConverter<ColumnJoinIndex<E>, Set<FilterRow<E>>>() { @Override public Set<FilterRow<E>> convert(ColumnJoinIndex<E> columnJoinIndex) { return columnJoinIndex.indexFilteredFilterRowSet(filterRow); } }); Set<FilterRow<E>> intersection = SetUtils.intersection(filterRowSetList); return intersection; } return this.filterRowSet; } } private static final class SelectExecution<E> implements TableExecution<ImmutableTable<E>, E> { private final ElementHolder<Table<E>> rettableElementHolder; private final List<Bucket<E>> closedBucketList; private final Class<E> componentType; private final List<ColumnJoin<E>> columnJoinList; private final List<TableSelect.Predicate<E>> predicateList; private final int top; private final int skip; private SelectExecution(ElementHolder<Table<E>> rettableElementHolder, List<Bucket<E>> closedBucketList, Class<E> componentType, List<ColumnJoin<E>> columnJoinList, List<Predicate<E>> predicateList, int top, int skip) { this.rettableElementHolder = rettableElementHolder; this.closedBucketList = closedBucketList; this.componentType = componentType; this.columnJoinList = columnJoinList; this.predicateList = predicateList; this.top = top; this.skip = skip; } @SuppressWarnings("unchecked") @Override public void execute(ImmutableTable<E> table) { // List<ColumnIdentity<E>> selectedColumnIdentityList = new ArrayList<ImmutableColumn.ColumnIdentity<E>>(); List<Iterable<? extends FilterRow<E>>> filteredFilterRowIterableList = new ArrayList<Iterable<? extends FilterRow<E>>>(); { final PreparedColumnJoin<E> preparedColumnJoin = new PreparedColumnJoin<E>(this.columnJoinList); for (Bucket<E> bucket : this.closedBucketList) { final Iterable<FilterRowIdentifiable<E>> filterRowIterable = bucket .newUnfilteredFilterRowIterable(); final Predicate<E> predicate = preparedColumnJoin.newIntersectionPredicate(bucket.getTable()); FilterRowFilterer<E> filterRowFilterer = new FilterRowFilterer<E>(filterRowIterable, ListUtils.add(bucket.getPredicateList(), predicate)); BitSet filterResult = filterRowFilterer.calculateFilterResult(); Iterable<FilterRowIdentifiable<E>> filteredFilterRowIterable = bucket .newFilteredFilterRowIterable(filterResult); filteredFilterRowIterableList.add(filteredFilterRowIterable); // selectedColumnIdentityList.addAll(bucket.getSelectedColumnIdentityList()); } } CrossProductFilterRowGenerator<E> crossProductFilterRowGenerator = new CrossProductFilterRowGenerator<E>( filteredFilterRowIterableList, this.columnJoinList); final List<E[]> elementArrayList = new ArrayList<E[]>(); final int resultRowStart = this.skip; final int resultRowEnd = this.top >= 0 ? this.top + this.skip - 1 : -1; int columnSize = 0; int rowCounter = 0; for (FilterRow<E> filterRow : crossProductFilterRowGenerator) { // boolean include = true; joinPredicateLoop: for (ColumnJoin<E> columnJoin : this.columnJoinList) { Set<ColumnIdentity<E>> columnIdentitySet = columnJoin.getColumnIdentitySet(); if (columnIdentitySet.size() > 1) { // final Iterator<ColumnIdentity<E>> iterator = columnIdentitySet.iterator(); final ColumnIdentity<E> columnIdentityFirst = iterator.next(); final E compareElement = filterRow.getElement(columnIdentityFirst); while (iterator.hasNext()) { final ColumnIdentity<E> columnIdentity = iterator.next(); final E element = filterRow.getElement(columnIdentity); if (!ObjectUtils.equals(compareElement, element)) { include = false; break joinPredicateLoop; } } } } // if (this.predicateList != null) { for (Predicate<E> predicate : this.predicateList) { include &= predicate.isIncluding(filterRow); if (!include) { break; } } } // if (include) { if (rowCounter >= resultRowStart && (resultRowEnd < 0 || rowCounter <= resultRowEnd)) { final List<E> elementList = new ArrayList<E>(); Map<E, AtomicInteger> columnIdentityToCounterMap = MapUtils.initializedCounterMap(); for (ColumnIdentity<E> columnIdentity : selectedColumnIdentityList) { final int counter = columnIdentityToCounterMap.get(columnIdentity).getAndIncrement(); E element = filterRow.getElement(columnIdentity, counter); elementList.add(element); } elementArrayList.add(elementList .toArray((E[]) Array.newInstance(this.componentType, elementList.size()))); columnSize = elementArrayList.size(); } rowCounter++; } } final E[][] elementMatrix = elementArrayList .toArray((E[][]) Array.newInstance(this.componentType, elementArrayList.size(), columnSize)); final Table<E> rettable = new ArrayTable<E>(elementMatrix); { final Set<String> tableNameSet = new LinkedHashSet<String>(); int columnIndex = 0; for (ColumnIdentity<E> columnIdentity : selectedColumnIdentityList) { final ImmutableColumn<E> column = columnIdentity.column(); final String tableName = column.table().getTableName(); String columnTitle = tableName + "." + column.getTitle(); rettable.setColumnTitle(columnIndex++, columnTitle); tableNameSet.add(tableName); } rettable.setTableName(CollectionUtils.toString(tableNameSet, new ElementConverterIdentity<String>(), Joiner.on(" "))); } this.rettableElementHolder.setElement(rettable); } } private static interface FilterRowIdentifiable<E> extends FilterRow<E> { public int rowIndex(); } public static class FilterRowFilterer<E> { private final Iterable<FilterRowIdentifiable<E>> filterRowIterable; private final Iterable<Predicate<E>> predicateIterable; public FilterRowFilterer(Iterable<FilterRowIdentifiable<E>> filterRowIterable, Iterable<Predicate<E>> predicateIterable) { super(); this.filterRowIterable = filterRowIterable; this.predicateIterable = predicateIterable; } public BitSet calculateFilterResult() { final BitSet retval = new BitSet(); for (FilterRowIdentifiable<E> filterRow : this.filterRowIterable) { // final int rowIndex = filterRow.rowIndex(); // boolean including = true; for (Predicate<E> predicate : this.predicateIterable) { if (!predicate.isIncluding(filterRow)) { including = false; break; } } // if (including) { retval.set(rowIndex); } } return retval; } } private static abstract class FilterRowAbstract<E> implements FilterRow<E> { @Override public E getElement(ImmutableTable<E> table, int columnIndex) { E retval = null; if (table != null) { ImmutableColumn<E> immutableColumn = table.column(columnIndex); if (immutableColumn != null) { ColumnIdentity<E> columnIdentity = immutableColumn.id(); retval = this.getElement(columnIdentity); } } return retval; } } private static class FilterRowComposite<E> extends FilterRowAbstract<E> { private final List<FilterRow<E>> filterRowList; public FilterRowComposite(List<FilterRow<E>> filterRowList) { super(); this.filterRowList = filterRowList; } @Override public E getElement(ColumnIdentity<E> columnIdentity) { final int skipNumber = 0; return this.getElement(columnIdentity, skipNumber); } @Override public boolean hasColumn(ColumnIdentity<E> columnIdentity) { for (FilterRow<E> filterRow : this.filterRowList) { final boolean hasColumn = filterRow.hasColumn(columnIdentity); if (hasColumn) { return true; } } return false; } @Override public E getElement(ColumnIdentity<E> columnIdentity, int skipNumber) { E retval = null; int matchCounter = 0; for (FilterRow<E> filterRow : this.filterRowList) { if (filterRow.hasColumn(columnIdentity)) { if (skipNumber == matchCounter) { final E element = filterRow.getElement(columnIdentity); retval = element; break; } matchCounter++; } } return retval; } } private static class CrossProductFilterRowGenerator<E> implements Iterable<FilterRow<E>> { private final Iterable<FilterRow<E>> filterRowIterableLeft; private final Iterable<FilterRow<E>> filterRowIterableRight; private final Iterable<ColumnJoin<E>> columnJoinIterable; @SuppressWarnings("unchecked") public CrossProductFilterRowGenerator(List<Iterable<? extends FilterRow<E>>> filterRowIterableList, Iterable<ColumnJoin<E>> columnJoinIterable) { super(); this.columnJoinIterable = columnJoinIterable; this.filterRowIterableLeft = (Iterable<FilterRow<E>>) ListUtils.firstElement(filterRowIterableList); final List<Iterable<? extends FilterRow<E>>> reducedFilterRowIteratorList = ListUtils .removeFirstToNewList(filterRowIterableList); if (reducedFilterRowIteratorList.size() > 1) { this.filterRowIterableRight = new CrossProductFilterRowGenerator<E>(reducedFilterRowIteratorList, columnJoinIterable); } else if (reducedFilterRowIteratorList.size() == 1) { this.filterRowIterableRight = (Iterable<FilterRow<E>>) ListUtils .firstElement(reducedFilterRowIteratorList); } else { this.filterRowIterableRight = null; } } @Override public Iterator<FilterRow<E>> iterator() { // if (this.filterRowIterableRight == null) { return this.filterRowIterableLeft.iterator(); } // final Iterator<FilterRow<E>> filterRowLeftIterator = this.filterRowIterableLeft.iterator(); final IndexBasedFilterRowIterableFactory<E> indexBasedFilterRowIterableFactory = new IndexBasedFilterRowIterableFactory<E>( this.columnJoinIterable, this.filterRowIterableRight); final FactoryParameterized<Iterator<FilterRow<E>>, FilterRow<E>> filterRowRightIteratorFactory = new FactoryParameterized<Iterator<FilterRow<E>>, FilterRow<E>>() { @Override public Iterator<FilterRow<E>> newInstance(FilterRow<E> filterRow) { return indexBasedFilterRowIterableFactory.newInstance(filterRow).iterator(); } }; return new Iterator<FilterRow<E>>() { private FilterRow<E> filterRowRight = null; private boolean resolved = false; private FilterRow<E> filterRowLeft = null; private Iterator<FilterRow<E>> filterRowRightIterator = null; @Override public boolean hasNext() { this.resolveNextFilterRowListIfUnresolved(); return this.filterRowRight != null && this.filterRowLeft != null; } private void resolveNextFilterRowListIfUnresolved() { while (!this.resolved) { this.filterRowRight = null; if (this.filterRowRightIterator != null && !this.filterRowRightIterator.hasNext()) { this.filterRowLeft = null; this.filterRowRightIterator = null; } if (this.filterRowLeft == null) { if (filterRowLeftIterator.hasNext()) { this.filterRowLeft = filterRowLeftIterator.next(); } } if (this.filterRowRightIterator == null) { this.filterRowRightIterator = filterRowRightIteratorFactory .newInstance(this.filterRowLeft); } if (this.filterRowRightIterator != null && this.filterRowRightIterator.hasNext()) { this.filterRowRight = this.filterRowRightIterator.next(); } else if (filterRowLeftIterator.hasNext()) { continue; } this.resolved = true; } } @SuppressWarnings("unchecked") @Override public FilterRow<E> next() { if (this.hasNext()) { this.resolved = false; return new FilterRowComposite<E>(Arrays.asList(this.filterRowLeft, this.filterRowRight)); } return null; } @Override public void remove() { throw new UnsupportedOperationException(); } }; } } /** * @author Omnaest * @param <E> */ private static class ColumnJoin<E> { private final Set<ColumnIdentity<E>> columnIdentitySet = new HashSet<ColumnIdentity<E>>(); public boolean contains(ColumnIdentity<E> columnIdentity) { return this.columnIdentitySet.contains(columnIdentity); } public boolean add(ColumnIdentity<E> columnIdentity) { return this.columnIdentitySet.add(columnIdentity); } public void mergeIntoThis(ColumnJoin<E> columnJoin) { if (columnJoin != null) { final Set<ColumnIdentity<E>> columnIdentitySet = columnJoin.getColumnIdentitySet(); this.columnIdentitySet.addAll(columnIdentitySet); columnIdentitySet.clear(); } } public Set<ColumnIdentity<E>> getColumnIdentitySet() { return this.columnIdentitySet; } } private static class PreparedColumnJoin<E> { private final Map<ColumnJoin<E>, Set<E>> columnJoinToIntersectionMap = new HashMap<ColumnJoin<E>, Set<E>>(); public PreparedColumnJoin(List<ColumnJoin<E>> columnJoinList) { super(); for (ColumnJoin<E> columnJoin : columnJoinList) { Set<ColumnIdentity<E>> columnIdentitySet = columnJoin.getColumnIdentitySet(); Set<Set<E>> setOfElementSet = SetUtils.convert(columnIdentitySet, new ElementConverter<ColumnIdentity<E>, Set<E>>() { @Override public Set<E> convert(ColumnIdentity<E> columnIdentity) { return columnIdentity.column().to().set(); } }); Set<E> intersection = SetUtils.intersection(setOfElementSet); this.columnJoinToIntersectionMap.put(columnJoin, intersection); } } private Map<ColumnIdentity<E>, Set<E>> getColumnIdentityToIntersectionMap(ImmutableTable<E> table) { final Map<ColumnIdentity<E>, Set<E>> retmap = new HashMap<ImmutableColumn.ColumnIdentity<E>, Set<E>>(); for (ColumnJoin<E> columnJoin : this.columnJoinToIntersectionMap.keySet()) { Set<ColumnIdentity<E>> columnIdentitySet = columnJoin.getColumnIdentitySet(); for (ColumnIdentity<E> columnIdentity : columnIdentitySet) { if (ObjectUtils.equals(table, columnIdentity.getTable())) { Set<E> intersection = this.columnJoinToIntersectionMap.get(columnJoin); retmap.put(columnIdentity, intersection); } } } return retmap; } public Predicate<E> newIntersectionPredicate(ImmutableTable<E> table) { final Map<ColumnIdentity<E>, Set<E>> columnIdentityToIntersectionMap = this .getColumnIdentityToIntersectionMap(table); return new Predicate<E>() { @Override public boolean isIncluding(FilterRow<E> row) { boolean retval = true; for (ColumnIdentity<E> columnIdentity : columnIdentityToIntersectionMap.keySet()) { Set<E> intersection = columnIdentityToIntersectionMap.get(columnIdentity); E element = row.getElement(columnIdentity); if (!intersection.contains(element)) { retval = false; break; } } return retval; } }; } } /** * Holds the currently modifiable join data * * @author Omnaest * @param <E> */ private static class Bucket<E> { private final class ElementConverterRowToFilterRow implements ElementConverter<ImmutableRow<E>, FilterRowIdentifiable<E>> { private final Set<ColumnIdentity<E>> columnIdentitySet; private ElementConverterRowToFilterRow(Set<ColumnIdentity<E>> columnIdentitySet) { this.columnIdentitySet = columnIdentitySet; } @Override public FilterRowIdentifiable<E> convert(final ImmutableRow<E> immutableRow) { final Set<ColumnIdentity<E>> columnIdentitySet = this.columnIdentitySet; return new FilterRowIdentifiable<E>() { @Override public E getElement(ColumnIdentity<E> columnIdentity) { E retval = null; if (columnIdentitySet.contains(columnIdentity)) { retval = immutableRow.getElement(columnIdentity.getColumnIndex()); } return retval; } @Override public E getElement(ImmutableTable<E> table, int columnIndex) { E retval = null; if (table != null) { ImmutableColumn<E> immutableColumn = table.column(columnIndex); if (immutableColumn != null) { ColumnIdentity<E> columnIdentity = immutableColumn.id(); retval = this.getElement(columnIdentity); } } return retval; } @Override public int rowIndex() { return immutableRow.index(); } @Override public boolean hasColumn(ColumnIdentity<E> columnIdentity) { return columnIdentitySet.contains(columnIdentity); } @Override public E getElement(ColumnIdentity<E> columnIdentity, int skipNumber) { return this.getElement(columnIdentity); } }; } } /* ************************************** Variables / State (internal/hiding) ************************************* */ private final ImmutableTable<E> table; private final List<ImmutableColumn<E>> columnList = new ArrayList<ImmutableColumn<E>>(); private List<Predicate<E>> predicateList = new ArrayList<Predicate<E>>(); /* *************************************************** Methods **************************************************** */ public Bucket(ImmutableTable<E> table) { super(); this.table = table; } public int columnSize() { return this.table.columnSize(); } public void addColumnByIndex(int columnIndex) { final ImmutableColumn<E> column = this.table.column(columnIndex); if (column != null) { this.columnList.add(column); } } public boolean add(TableSelect.Predicate<E> e) { return this.predicateList.add(e); } public Iterable<FilterRowIdentifiable<E>> newUnfilteredFilterRowIterable() { final Set<ColumnIdentity<E>> columnIdentitySet = this.determineColumnIdentitySet(); return IterableUtils.adapter(this.table, new ElementConverterRowToFilterRow(columnIdentitySet)); } public Iterable<FilterRowIdentifiable<E>> newFilteredFilterRowIterable(final BitSet filterResult) { final Set<ColumnIdentity<E>> columnIdentitySet = this.determineColumnIdentitySet(); final ImmutableTable<E> table = this.table; final Iterable<ImmutableRow<E>> rowIterable = new Iterable<ImmutableRow<E>>() { @Override public Iterator<ImmutableRow<E>> iterator() { return new Iterator<ImmutableRow<E>>() { private int index = filterResult.nextSetBit(0); private boolean resolved = true; @Override public boolean hasNext() { if (!this.resolved) { this.index = filterResult.nextSetBit(this.index + 1); this.resolved = true; } return this.index >= 0; } @Override public ImmutableRow<E> next() { if (this.hasNext()) { this.resolved = false; return table.row(this.index); } throw new NoSuchElementException(); } @Override public void remove() { throw new UnsupportedOperationException(); } }; } }; return IterableUtils.adapter(rowIterable, new ElementConverterRowToFilterRow(columnIdentitySet)); } private Set<ColumnIdentity<E>> determineColumnIdentitySet() { final Set<ColumnIdentity<E>> columnIdentitySet = new HashSet<ImmutableColumn.ColumnIdentity<E>>(); for (ImmutableColumn<E> immutableColumn : this.table.columns()) { columnIdentitySet.add(immutableColumn.id()); } return columnIdentitySet; } public ImmutableTable<E> getTable() { return this.table; } public List<Predicate<E>> getPredicateList() { return this.predicateList; } public List<ColumnIdentity<E>> getSelectedColumnIdentityList() { final List<ColumnIdentity<E>> retlist = new ArrayList<ColumnIdentity<E>>(); for (ImmutableColumn<E> immutableColumn : this.columnList) { retlist.add(immutableColumn.id()); } return retlist; } } /* *************************************************** Methods **************************************************** */ public TableSelectImpl(Table<E> table) { this.bucket = new Bucket<E>(table); } @Override public TableJoin<E> withTableLock(boolean lockEnabled) { this.tableForLockingSet.add(this.bucket.getTable()); return this; } @Override public TableJoin<E> allColumns() { return this.columns(0, new Range(1, this.bucket.columnSize() - 1).toIntArray()); } @Override public TableSelect<E> allColumns(ImmutableTable<E> table) { for (ImmutableColumn<E> column : table.columns()) { this.column(column); } return this; } @Override public TableSelect<E> column(ImmutableTable<E> table, int columnIndex) { final Set<Bucket<E>> bucketSet = this.determineBucketSetFor(table); for (Bucket<E> bucket : bucketSet) { bucket.addColumnByIndex(columnIndex); } return this; } private Set<Bucket<E>> determineBucketSetFor(ImmutableTable<E> table) { final Set<Bucket<E>> bucketSet = new HashSet<Bucket<E>>(); { List<Bucket<E>> bucketList = ListUtils.addToNewList(this.closedBucketList, this.bucket); for (Bucket<E> bucket : bucketList) { if (ObjectUtils.equals(table, bucket.getTable())) { bucketSet.add(bucket); } } } return bucketSet; } @Override public TableSelect<E> column(int columnIndex) { this.bucket.addColumnByIndex(columnIndex); return this; } @Override public TableSelect<E> column(ImmutableColumn<E> column) { if (column != null) { this.column(column.table(), column.index()); } return this; } @Override public TableJoin<E> columns(int columnIndex, int... columnIndexes) { this.bucket.addColumnByIndex(columnIndex); for (int iColumnIndex : columnIndexes) { this.bucket.addColumnByIndex(iColumnIndex); } return this; } @Override public TableSelect.TableJoin<E> join(ImmutableTable<E> table) { Assert.isNotNull(table, "table must not be null if joined"); this.closeAndRolloverBucket(table); return this; } private void closeAndRolloverBucket(ImmutableTable<E> table) { this.closedBucketList.add(this.bucket); this.bucket = new Bucket<E>(table); } @Override public TableSelect<E> where(TableSelect.Predicate<E> predicate, TableSelect.Predicate<E>... predicates) { if (predicate != null) { this.predicateList.add(predicate); } for (Predicate<E> iPredicate : predicates) { if (iPredicate != null) { this.predicateList.add(iPredicate); } } return this; } @Override public TableSelect.TableSelectExecution<E> as() { this.closeAndRolloverBucket(null); return this; } @Override public TableJoin<E> onEqual(ImmutableColumn<E> columnLeft, ImmutableColumn<E> columnRight) { if (columnLeft != null && columnRight != null) { ColumnIdentity<E> columnIdentityLeft = columnLeft.id(); ColumnIdentity<E> columnIdentityRight = columnRight.id(); boolean addedToExistingJoin = false; ColumnJoin<E> columnJoinResolved = null; List<ColumnJoin<E>> removableColumnJoinList = new ArrayList<ColumnJoin<E>>(); for (ColumnJoin<E> columnJoin : this.columnJoinList) { if (columnJoin.contains(columnIdentityLeft) || columnJoin.contains(columnIdentityRight)) { if (!addedToExistingJoin) { columnJoin.add(columnIdentityRight); columnJoin.add(columnIdentityLeft); addedToExistingJoin = true; columnJoinResolved = columnJoin; } else { columnJoinResolved.mergeIntoThis(columnJoin); removableColumnJoinList.add(columnJoin); } } } if (!addedToExistingJoin) { final ColumnJoin<E> columnJoin = new ColumnJoin<E>(); columnJoin.add(columnIdentityRight); columnJoin.add(columnIdentityLeft); this.columnJoinList.add(columnJoin); } this.columnJoinList.removeAll(removableColumnJoinList); } return this; } @Override public TableJoin<E> on(TableSelect.Predicate<E> predicate) { if (predicate != null) { this.bucket.add(predicate); } return this; } /* (non-Javadoc) * @see org.omnaest.utils.table.TableSelect.TableSelectExecution#table() */ @SuppressWarnings("unchecked") @Override public Table<E> table() { // final Class<E> componentType = ListUtils.firstElement(this.closedBucketList).getTable().elementType(); final ElementHolder<Table<E>> rettableElementHolder = new ElementHolder<Table<E>>(); final List<Bucket<E>> closedBucketList = this.closedBucketList; final List<ColumnJoin<E>> columnJoinList = this.columnJoinList; final SelectExecution<E> tableExecution = new SelectExecution<E>(rettableElementHolder, closedBucketList, componentType, columnJoinList, this.predicateList, this.top, this.skip); Set<ImmutableTable<E>> tableForLockingSet = this.tableForLockingSet; if (tableForLockingSet.isEmpty()) { tableExecution.execute(null); } else { final ImmutableTable<E> table = IterableUtils.firstElement(tableForLockingSet); final ImmutableTable<E>[] furtherLockedTables = tableForLockingSet.size() > 1 ? Arrays.copyOfRange(ArrayUtils.valueOf(tableForLockingSet, ImmutableTable.class), 1, tableForLockingSet.size()) : new ImmutableTable[0]; table.executeWithReadLock(tableExecution, furtherLockedTables); } return rettableElementHolder.getElement(); } @Override public SortedMap<E, Set<Row<E>>> sortedMap() { // final SortedMap<E, Set<Row<E>>> retmap = new TreeMap<E, Set<Row<E>>>(); Table<E> table = this.table(); for (Row<E> row : table.rows()) { // final E key = row.getElement(0); Set<Row<E>> set = retmap.get(key); if (set == null) { set = new LinkedHashSet<Row<E>>(); retmap.put(key, set); } set.add(row); } return retmap; } @Override public org.omnaest.utils.table.TableSelect.TableJoin<E> onEqual(final ImmutableColumn<E> column, final E value) { final ColumnIdentity<E> columnIdentity = column.id(); final Predicate<E> predicate = new PredicateEqualValue<E>(value, columnIdentity); return this.on(predicate); } @Override public org.omnaest.utils.table.TableSelect.TableJoin<E> onWithin(final ImmutableColumn<E> column, final Set<E> valueSet) { final ColumnIdentity<E> columnIdentity = column.id(); final Predicate<E> predicate = new PredicateWithin<E>(columnIdentity, valueSet); return this.on(predicate); } @Override public org.omnaest.utils.table.TableSelect.TableJoin<E> onLike(final ImmutableColumn<E> column, final Pattern pattern) { final ColumnIdentity<E> columnIdentity = column.id(); final Predicate<E> predicate = new PredicateLike<E>(columnIdentity, pattern); return this.on(predicate); } @Override public TableSelect<E> whereEqual(ImmutableColumn<E> column, E value) { final ColumnIdentity<E> columnIdentity = column.id(); final Predicate<E> predicate = new PredicateEqualValue<E>(value, columnIdentity); return this.where(predicate); } @Override public TableSelect<E> whereWithin(ImmutableColumn<E> column, Set<E> valueSet) { final ColumnIdentity<E> columnIdentity = column.id(); final Predicate<E> predicate = new PredicateWithin<E>(columnIdentity, valueSet); return this.where(predicate); } @Override public TableSelect<E> whereLike(ImmutableColumn<E> column, Pattern pattern) { final ColumnIdentity<E> columnIdentity = column.id(); final Predicate<E> predicate = new PredicateLike<E>(columnIdentity, pattern); return this.where(predicate); } @SuppressWarnings("unchecked") @Override public TableSelect<E> where(org.omnaest.utils.table.TableSelect.Predicate<E> predicate) { Predicate<E>[] predicates = new Predicate[0]; return this.where(predicate, predicates); } @Override public TableSelect<E> top(int top) { this.top = top; return this; } @Override public TableSelect<E> skip(int skip) { this.skip = skip; return this; } }