Package xbird.xquery.expr.opt

Source Code of xbird.xquery.expr.opt.Join

/*
* @(#)$Id: Join.java 3619 2008-03-26 07:23:03Z yui $
*
* Copyright 2006-2008 Makoto YUI
*
* 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.
*
* Contributors:
*     Makoto YUI - initial implementation
*/
package xbird.xquery.expr.opt;

import static xbird.xquery.expr.comp.GeneralComp.Operator.EQ;
import static xbird.xquery.expr.comp.GeneralComp.Operator.GE;
import static xbird.xquery.expr.comp.GeneralComp.Operator.GT;
import static xbird.xquery.expr.comp.GeneralComp.Operator.LE;
import static xbird.xquery.expr.comp.GeneralComp.Operator.LT;
import static xbird.xquery.expr.comp.GeneralComp.Operator.NE;
import static xbird.xquery.expr.comp.NodeComp.Operator.FOLLOWS;
import static xbird.xquery.expr.comp.NodeComp.Operator.IS;
import static xbird.xquery.expr.comp.NodeComp.Operator.PRECEDES;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.WeakHashMap;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import xbird.util.iterator.NestedIterator;
import xbird.xquery.XQueryException;
import xbird.xquery.dm.value.AbstractSequence;
import xbird.xquery.dm.value.AtomicValue;
import xbird.xquery.dm.value.Item;
import xbird.xquery.dm.value.Sequence;
import xbird.xquery.dm.value.literal.XString;
import xbird.xquery.dm.value.sequence.SingleCollection;
import xbird.xquery.dm.value.sequence.ValueSequence;
import xbird.xquery.expr.AbstractXQExpression;
import xbird.xquery.expr.XQExpression;
import xbird.xquery.expr.comp.ComparisonOp;
import xbird.xquery.expr.comp.GeneralComp;
import xbird.xquery.expr.comp.NodeComp;
import xbird.xquery.expr.flwr.Binding;
import xbird.xquery.expr.flwr.FLWRExpr;
import xbird.xquery.expr.flwr.ForClause;
import xbird.xquery.expr.flwr.LetClause;
import xbird.xquery.expr.path.FilterExpr;
import xbird.xquery.expr.path.FilterExpr.ConditionalFilter;
import xbird.xquery.expr.path.PathExpr.CompositePath;
import xbird.xquery.expr.var.BindingVariable;
import xbird.xquery.expr.var.VarRef;
import xbird.xquery.expr.var.Variable;
import xbird.xquery.expr.var.BindingVariable.AnonymousLetVariable;
import xbird.xquery.expr.var.BindingVariable.LetVariable;
import xbird.xquery.meta.DynamicContext;
import xbird.xquery.meta.Focus;
import xbird.xquery.meta.IFocus;
import xbird.xquery.meta.StaticContext;
import xbird.xquery.meta.XQueryContext;
import xbird.xquery.parser.XQueryParserVisitor;
import xbird.xquery.parser.visitor.AbstractXQueryParserVisitor;
import xbird.xquery.type.AtomicType;
import xbird.xquery.type.Type;
import xbird.xquery.type.node.NodeType;
import xbird.xquery.type.xs.StringType;

/**
*
* <DIV lang="en"></DIV>
* <DIV lang="ja"></DIV>
*
* @author Makoto YUI (yuin405+xbird@gmail.com)
*/
public final class Join {

    private static final boolean ENV_DISABLE_JOIN_THREADING = System.getProperty("xbird.optim.enable_join_threading") == null;

    private Join() {}

    public static final class JoinDetector extends AbstractXQueryParserVisitor {

        private List<Binding> _currentBindings;
        private ForClause _currentForClause;
        private PathVariable _lastPathVariable;

        public JoinDetector() {
            super();
        }

        @Override
        public XQExpression visit(FLWRExpr flwr, XQueryContext ctxt) throws XQueryException {
            this._currentBindings = flwr.getClauses();
            super.visit(flwr, ctxt);
            this._currentBindings = null;
            return flwr;
        }

        @Override
        public ForClause visit(ForClause clause, XQueryContext ctxt) throws XQueryException {
            this._currentForClause = clause;
            super.visit(clause, ctxt);
            this._currentForClause = null;
            return clause;
        }

        @Override
        public XQExpression visit(PathVariable variable, XQueryContext ctxt) throws XQueryException {
            this._lastPathVariable = variable;
            super.visit(variable, ctxt);
            this._lastPathVariable = null;
            return variable;
        }

        @Override
        public XQExpression visit(CompositePath fragment, XQueryContext ctxt)
                throws XQueryException {
            PathVariable prevVar = _lastPathVariable;
            Variable src = fragment.getSourceVariable();
            src.visit(this, ctxt);
            this._lastPathVariable = prevVar;
            XQExpression filter = fragment.getFilterExpr();
            filter.visit(this, ctxt);
            return fragment;
        }

        @Override
        public XQExpression visit(FilterExpr filterExpr, XQueryContext ctxt) throws XQueryException {
            boolean isFirst = true;
            int shipId = -1;
            List<XQExpression> srcFilter = Collections.<XQExpression> emptyList();
            List<XQExpression> persistKeyExprs = Collections.<XQExpression> emptyList();
            List<XQExpression> searchKeyExprs = Collections.<XQExpression> emptyList();
            final List<XQExpression> predicates = filterExpr.getPredicates();
            String opr = null;
            for(XQExpression p : predicates) {
                if(p instanceof ConditionalFilter) {
                    XQExpression pred = ((ConditionalFilter) p).getPredicate();
                    if((pred instanceof GeneralComp) || (pred instanceof NodeComp)) {
                        ComparisonOp cmpOp = (ComparisonOp) pred;
                        String newOpr = cmpOp.getOperator();
                        if(opr != null && opr != newOpr) {
                            break;
                        }
                        opr = newOpr;
                        XQExpression left = cmpOp.getLeftOperand();
                        if(left instanceof VarRef) {
                            left = ((VarRef) left).getValue();
                        }
                        if(left instanceof PathVariable) {
                            left = ((PathVariable) left).getValue();
                        }
                        XQExpression right = cmpOp.getRightOperand();
                        if(right instanceof VarRef) {
                            right = ((VarRef) right).getValue();
                        }
                        if(right instanceof PathVariable) {
                            right = ((PathVariable) right).getValue();
                        }
                        XQExpression leftSrc = left, rightSrc = right;
                        while(leftSrc instanceof CompositePath) {
                            leftSrc = ((CompositePath) leftSrc).getSourceExpr();
                        }
                        while(rightSrc instanceof CompositePath) {
                            rightSrc = ((CompositePath) rightSrc).getSourceExpr();
                        }
                        VarRef pathSrc = null;
                        XQExpression persistKeyExpr = null, searchKeyExpr = null;
                        if(leftSrc instanceof VarRef && !(rightSrc instanceof VarRef)) {
                            pathSrc = (VarRef) leftSrc;
                            persistKeyExpr = right;
                            searchKeyExpr = left;
                        } else if(rightSrc instanceof VarRef && !(leftSrc instanceof VarRef)) {
                            pathSrc = (VarRef) rightSrc;
                            persistKeyExpr = left;
                            searchKeyExpr = right;
                        }
                        if(pathSrc != null) {
                            if(isFirst) {
                                persistKeyExprs = new ArrayList<XQExpression>(4);
                                searchKeyExprs = new ArrayList<XQExpression>(4);
                            }
                            Variable pv = pathSrc.getValue();
                            if(pv instanceof BindingVariable) {
                                if(searchKeyExpr instanceof CompositePath
                                        && _currentForClause != null) {
                                    assert (_currentBindings != null);
                                    final LetVariable lv = new AnonymousLetVariable(searchKeyExpr);
                                    lv.incrementReferenceCount();
                                    final int bsize = _currentBindings.size();
                                    assert (bsize > 0) : bsize;
                                    boolean found = false;
                                    for(int i = 0; i < bsize; i++) {
                                        Binding binding = _currentBindings.get(i);
                                        if(binding == _currentForClause) {
                                            LetClause lc = new LetClause(lv);
                                            _currentBindings.add(i, lc);
                                            found = true;
                                            break;
                                        }
                                    }
                                    assert (found);
                                    searchKeyExpr = lv;
                                }
                                persistKeyExprs.add(persistKeyExpr);
                                searchKeyExprs.add(searchKeyExpr);
                                isFirst = false;
                                continue;
                            } else if(pv instanceof ShippedVariable) {
                                shipId = ((ShippedVariable) pv).getIdentifier();
                                persistKeyExprs.add(persistKeyExpr);
                                searchKeyExprs.add(searchKeyExpr);
                                isFirst = false;
                                continue;
                            }
                        }
                        if(isFirst) {
                            if(srcFilter == Collections.EMPTY_LIST) {
                                srcFilter = new ArrayList<XQExpression>(4);
                            }
                            srcFilter.add(p);
                        } else {
                            break;
                        }
                    }
                }
            }
            if(!isFirst && _lastPathVariable != null) { // found join
                XQExpression pvExpr = _lastPathVariable.getValue();
                final XQExpression srcExpr;
                if(pvExpr instanceof CompositePath) {
                    CompositePath srcPath = (CompositePath) pvExpr;
                    XQExpression filterBaseExpr = filterExpr.getSourceExpr();
                    srcPath.setFilterExpr(filterBaseExpr);
                    srcExpr = srcPath;
                } else if(pvExpr instanceof FilterExpr) {//FIXME REVIEWME
                    srcExpr = filterExpr.getSourceExpr();
                } else {
                    throw new IllegalStateException("Unexpected expression is binded to last PathVariable "
                            + _lastPathVariable + ": " + pvExpr);
                }
                PromoteJoinExpression promoteJoinExpr = new PromoteJoinExpression(srcExpr, srcFilter, persistKeyExprs, opr, searchKeyExprs);
                promoteJoinExpr._shippedId = shipId;
                _lastPathVariable.setValue(promoteJoinExpr);
                _lastPathVariable = null;
            }
            return filterExpr;
        }
    }

    public static final class PromoteJoinExpression extends AbstractXQExpression {
        private static final long serialVersionUID = 2994212583454668688L;
        // FIXME remove the use of WeakHashMap
        private static transient final Map<Integer, JoinTable> _tables = new WeakHashMap<Integer, JoinTable>(4);

        private XQExpression _srcExpr;
        private final List<XQExpression> _srcFilters;
        private final List<XQExpression> _pkeyExprs;
        private final List<Type> _keyTypes;
        private final String _opr;
        private final List<XQExpression> _skeyExprs;
        private JoinTable _joinTable = null;
        private final boolean _isNodeComp;

        private int _shippedId = -1;

        public PromoteJoinExpression(XQExpression srcExpr, List<XQExpression> srcFilter, List<XQExpression> persistKeyExprs, String opr, List<XQExpression> searchKeyExprs) {
            if(opr == null) {
                throw new IllegalArgumentException("Operator is not set");
            }
            if(persistKeyExprs.size() != searchKeyExprs.size()) {
                throw new IllegalArgumentException("Keys length is different.\npersistentKeyExpr: "
                        + persistKeyExprs + ", searchKeys: " + searchKeyExprs);
            }
            this._srcExpr = srcExpr;
            this._srcFilters = srcFilter;
            this._opr = opr;
            this._pkeyExprs = persistKeyExprs;
            this._skeyExprs = searchKeyExprs;
            final List<Type> keyTypes = new ArrayList<Type>(persistKeyExprs.size());
            for(XQExpression keyexpr : persistKeyExprs) {
                final Type t = keyexpr.getType();
                if(!(t instanceof NodeType) && !(t instanceof AtomicType)) {
                    keyTypes.add(StringType.STRING);
                } else {
                    keyTypes.add(t);
                }
            }
            this._keyTypes = keyTypes;
            if(srcFilter.isEmpty()) {
                this._type = srcExpr.getType();
            } else {
                final int last = srcFilter.size() - 1;
                this._type = srcFilter.get(last).getType();
            }
            this._isNodeComp = (opr == IS.sgn || opr == PRECEDES.sgn || opr == FOLLOWS.sgn);
        }

        public XQExpression getSrcExpr() {
            return _srcExpr;
        }

        public void setSrcExpr(XQExpression src) {
            this._srcExpr = src;
        }

        public List<XQExpression> getPersistKeyExprs() {
            return _pkeyExprs;
        }

        public List<XQExpression> getSearchKeyExprs() {
            return _skeyExprs;
        }

        public List<XQExpression> getFilterExprs() {
            return _srcFilters;
        }

        @Override
        public String toString() {
            final StringBuilder buf = new StringBuilder(128);
            buf.append(_srcExpr.toString());
            for(XQExpression filter : _srcFilters) {
                buf.append('[');
                buf.append(filter.toString());
                buf.append(']');
            }
            final int condsize = _pkeyExprs.size();
            for(int i = 0; i < condsize; i++) {
                buf.append('[');
                buf.append(_pkeyExprs.get(i));
                buf.append(' ');
                buf.append(_opr);
                buf.append(' ');
                buf.append(_skeyExprs.get(i));
                buf.append(']');
            }
            return buf.toString();
        }

        public XQExpression visit(XQueryParserVisitor visitor, XQueryContext ctxt)
                throws XQueryException {
            return visitor.visit(this, ctxt);
        }

        public XQExpression staticAnalysis(StaticContext statEnv) throws XQueryException {
            return this;
        }

        public Sequence<? extends Item> eval(final Sequence<? extends Item> contextSeq, final DynamicContext dynEnv)
                throws XQueryException {
            final Map<Integer, JoinTable> tables = _tables;
            final int shippedId = _shippedId;
            if(shippedId != -1) {
                this._joinTable = tables.get(shippedId);
            }
            if(_joinTable != null) {
                return map(contextSeq, dynEnv);
            }
            if(ENV_DISABLE_JOIN_THREADING) {
                final JoinTable joinTable = makeJoinTable(contextSeq, dynEnv);
                this._joinTable = joinTable;
                if(shippedId != -1) {
                    tables.put(shippedId, joinTable);
                }
                return map(contextSeq, dynEnv);
            }
            // threaded execution
            final ExecutorService ex = Executors.newSingleThreadExecutor();
            final Callable<JoinTable> call = new Callable<JoinTable>() {
                public JoinTable call() throws Exception {
                    final JoinTable joinTable = makeJoinTable(contextSeq, dynEnv);
                    if(shippedId != -1) {
                        tables.put(shippedId, joinTable);
                    }
                    return joinTable;
                }
            };
            final Future<JoinTable> future = ex.submit(call);
            final Sequence<? extends Item> mapped;
            try {
                mapped = map(contextSeq, dynEnv, future);
            } catch (InterruptedException e) {
                ex.shutdownNow();
                throw new IllegalStateException(e);
            } catch (ExecutionException e) {
                ex.shutdownNow();
                throw new IllegalStateException(e);
            }
            ex.shutdown(); // REVIEWME is really required?
            return mapped;
        }

        protected JoinTable makeJoinTable(Sequence<? extends Item> contextSeq, DynamicContext dynEnv)
                throws XQueryException {
            Sequence<? extends Item> src = _srcExpr.eval(contextSeq, dynEnv);
            for(XQExpression f : _srcFilters) {
                src = f.eval(src, dynEnv);
            }
            final int keysize = _pkeyExprs.size();
            assert (keysize > 0);
            final JoinTable joinTable = new JoinTable(_opr, _type);
            final IFocus<? extends Item> srcItor = src.iterator();
            int counter = 0;
            outer: for(Item it : srcItor) {
                final Comparable[] persistKeys = new Comparable[keysize];
                if(keysize == 1) {
                    final XQExpression key = _pkeyExprs.get(0);
                    final IFocus<? extends Item> pkvItor = key.eval(it, dynEnv).iterator();
                    for(Item fpv : pkvItor) {
                        if(_isNodeComp || fpv instanceof AtomicValue) {
                            persistKeys[0] = fpv;
                        } else {
                            if(counter == 0) {
                                _keyTypes.set(0, StringType.STRING);
                            }
                            persistKeys[0] = new XString(fpv.stringValue());
                        }
                        joinTable.put(persistKeys, it);
                    }
                    pkvItor.closeQuietly();
                } else {// TODO FIXME multiple join keys   
                    for(int i = 0; i < keysize; i++) {
                        final XQExpression key = _pkeyExprs.get(i);
                        final Sequence<? extends Item> pkv = key.eval(it, dynEnv);
                        final Iterator<? extends Item> pkvItor = pkv.iterator();
                        if(!pkvItor.hasNext()) {
                            continue outer;
                        }
                        final Item fpv = pkvItor.next();
                        if(pkvItor.hasNext()) {
                            persistKeys[i] = new SingleCollection(pkv, dynEnv);
                        } else if(_isNodeComp || fpv instanceof AtomicValue) {
                            persistKeys[i] = fpv;
                        } else {
                            if(counter == 0) {
                                _keyTypes.set(i, StringType.STRING);
                            }
                            persistKeys[i] = new XString(fpv.stringValue());
                        }
                    }
                    joinTable.put(persistKeys, it);
                }
                counter++;
            }
            srcItor.closeQuietly();
            return joinTable;
        }

        protected ValueSequence map(Sequence<? extends Item> contextSeq, DynamicContext dynEnv)
                throws XQueryException {
            final int searchKeyLen = _skeyExprs.size();
            Iterator<? extends Item> skvItor = null;
            for(int i = 0; i < searchKeyLen; i++) {
                final Sequence<? extends Item> skv = _skeyExprs.get(i).eval(contextSeq, dynEnv);
                if(skvItor == null) {
                    skvItor = skv.iterator();
                } else {
                    skvItor = new NestedIterator<Item>(skvItor, skv.iterator());
                }
            }
            JoinTable joinTable = _joinTable;
            final ValueSequence vs = new ValueSequence(dynEnv);
            while(skvItor.hasNext()) {
                final Comparable searchKey;
                if(searchKeyLen == 1) {
                    final Item fsv = skvItor.next();
                    searchKey = mapArgument(fsv, 0, dynEnv);
                } else {
                    final Comparable[] searchKeys = new Comparable[searchKeyLen];
                    for(int i = 0; i < searchKeyLen; i++) {
                        Item fsv = skvItor.next();
                        searchKeys[i] = mapArgument(fsv, i, dynEnv);
                    }
                    searchKey = new JoinTable.Key(searchKeys, _opr);
                }
                final IFocus<Item> jsItor = joinTable.entrySequence(searchKey, dynEnv).iterator();
                for(Item entry : jsItor) {
                    vs.addItem(entry);
                }
                jsItor.closeQuietly();
            }
            if(vs.isEmpty()) {
                return ValueSequence.EMPTY_SEQUENCE;
            }
            return vs;
        }

        private Item mapArgument(Item item, int index, DynamicContext dynEnv)
                throws XQueryException {
            if(_isNodeComp) {
                return item;
            }
            final Type t = _keyTypes.get(index);
            if(t == StringType.STRING) {
                return new XString(item.stringValue());
            }
            final AtomicValue it = (item instanceof AtomicValue) ? (AtomicValue) item
                    : new XString(item.stringValue());
            if(t instanceof AtomicType) {
                final AtomicType trgType = (AtomicType) t;
                final AtomicValue converted = it.castAs(trgType, dynEnv);
                return converted;
            }
            return it;
        }

        protected Sequence<? extends Item> map(Sequence<? extends Item> contextSeq, DynamicContext dynEnv, Future<JoinTable> future)
                throws XQueryException, InterruptedException, ExecutionException {
            final int searchKeyLen = _skeyExprs.size();
            Iterator<? extends Item> skvItor = null;
            for(int i = 0; i < searchKeyLen; i++) {
                Sequence<? extends Item> skv = _skeyExprs.get(i).eval(contextSeq, dynEnv);
                final Iterator<? extends Item> itor = skv.iterator();//skv.materialize().iterator();
                if(skvItor == null) {
                    skvItor = itor;
                } else {
                    skvItor = new NestedIterator<Item>(skvItor, itor);
                }
            }
            final ValueSequence vs = new ValueSequence(dynEnv);
            while(skvItor.hasNext()) {
                final Comparable searchKey;
                if(searchKeyLen == 1) {
                    final Item fsv = skvItor.next();
                    searchKey = mapArgument(fsv, 0, dynEnv);
                } else {
                    final Comparable[] searchKeys = new Comparable[searchKeyLen];
                    for(int i = 0; i < searchKeyLen; i++) {
                        Item fsv = skvItor.next();
                        searchKeys[i] = mapArgument(fsv, i, dynEnv);
                    }
                    searchKey = new JoinTable.Key(searchKeys, _opr);
                }
                if(_joinTable == null) {
                    this._joinTable = future.get();
                }
                final IFocus<Item> js = _joinTable.entrySequence(searchKey, dynEnv).iterator();
                for(Item entry : js) {
                    vs.addItem(entry);
                }
                js.closeQuietly();
            }
            if(vs.isEmpty()) {
                return ValueSequence.EMPTY_SEQUENCE;
            }
            return vs;
        }
    }

    private static final class JoinTable implements Serializable {
        private static final long serialVersionUID = -1029891058586613017L;

        private final Map<Comparable, Entry> _table = new HashMap<Comparable, Entry>(32, 0.8f);
        private final SortedSet<Comparable> _orderedKey;
        private final Key _probe;
        private final String _opr;
        private final boolean _equiJoin;
        private final Type _type;

        public JoinTable(String opr, Type type) {
            if(opr == null || type == null) {
                throw new IllegalArgumentException();
            }
            this._opr = opr;
            if(EQ.sgn.equals(opr) || IS.sgn.equals(opr)) {
                this._equiJoin = true;
                this._orderedKey = null;
            } else {
                this._equiJoin = false;
                this._orderedKey = new TreeSet<Comparable>();
            }
            this._type = type;
            this._probe = new Key(null, opr);
        }

        public static final class Key implements Comparable, Serializable {
            private static final long serialVersionUID = -7951500027264400851L;

            private Comparable[] keys;
            private final int[] expected;
            private final int unexpected;

            public Key(Comparable[] keys, String opr) {
                this.keys = keys;
                if(EQ.sgn.equals(opr) || IS.sgn.equals(opr)) {
                    this.expected = new int[] { 0 };
                    this.unexpected = -1;
                } else if(NE.sgn.equals(opr)) {
                    this.expected = new int[] { 1, -1 };
                    this.unexpected = 0;
                } else if(GT.sgn.equals(opr) || FOLLOWS.sgn.equals(opr)) {
                    this.expected = new int[] { 1 };
                    this.unexpected = -1;
                } else if(LT.sgn.equals(opr) || PRECEDES.sgn.equals(opr)) {
                    this.expected = new int[] { -1 };
                    this.unexpected = 1;
                } else if(GE.sgn.equals(opr)) {
                    this.expected = new int[] { 1, 0 };
                    this.unexpected = -1;
                } else if(LE.sgn.equals(opr)) {
                    this.expected = new int[] { 0, -1 };
                    this.unexpected = 1;
                } else {
                    throw new IllegalArgumentException("Illegal operator: " + opr);
                }
            }

            public int compareTo(Object o) {
                if(!(o instanceof Key)) {
                    throw new IllegalStateException("Invalid key class: " + o.getClass().getName());
                }
                final Key key2 = (Key) o;
                assert (keys.length > 0 && keys.length == key2.keys.length);
                for(int i = 0; i < keys.length; i++) {
                    final Comparable c1 = keys[i];
                    final Comparable c2 = key2.keys[i];
                    final int cmp = c1.compareTo(c2);
                    // hack
                    boolean found = false;
                    for(int c : expected) {
                        if(c == cmp) {
                            found = true;
                        }
                    }
                    if(!found) {
                        return unexpected;
                    }
                }
                return expected[0];
            }

            @Override
            public int hashCode() {
                return Arrays.hashCode(keys);
            }

            @Override
            public boolean equals(Object obj) {
                if(obj instanceof Key) {
                    return Arrays.equals(keys, ((Key) obj).keys);
                }
                return false;
            }

            @Override
            public String toString() {
                if(keys == null) {
                    return "{}";
                }
                final StringBuilder buf = new StringBuilder(64);
                buf.append('{');
                final int size = keys.length;
                for(int i = 0; i < size; i++) {
                    if(i != 0) {
                        buf.append(',');
                    }
                    buf.append(keys[i].toString());
                }
                buf.append('}');
                return buf.toString();
            }
        }

        public static final class Entry implements Serializable {
            private static final long serialVersionUID = -6288377491722537673L;

            private Item[] values;
            private int count;

            public Entry(final Item value) {
                assert (value != null);
                this.values = new Item[] { value, null };
                this.count = 1;
            }

            public void add(Item it) {
                if(count >= values.length) {
                    final Item[] nv = new Item[values.length * 2];
                    System.arraycopy(values, 0, nv, 0, values.length);
                    this.values = nv;
                }
                values[count] = it;
                ++count;
            }

            public Item[] getItems() {
                if(values.length == count) {
                    return values;
                } else {
                    Item[] newItems = new Item[count];
                    System.arraycopy(values, 0, newItems, 0, count);
                    this.values = newItems;
                    return newItems;
                }
            }

            @Override
            public int hashCode() {
                return Arrays.hashCode(values);
            }

            @Override
            public boolean equals(Object obj) {
                if(obj instanceof Entry) {
                    return Arrays.equals(values, ((Entry) obj).values);
                }
                return false;
            }

            @Override
            public String toString() {
                final StringBuilder buf = new StringBuilder(128);
                for(int i = 0; i < count; i++) {
                    buf.append(values[i].toString());
                    if(i != (count - 1)) {
                        buf.append(",\n");
                    }
                }
                return buf.toString();
            }
        }

        private static final class JoinEntrySequence extends AbstractSequence<Item> {
            private static final long serialVersionUID = 5987456615844113724L;

            private final Map<Comparable, Entry> _table;
            private final SortedSet<Comparable> _sortedKeys;
            private final Type _type;

            public JoinEntrySequence(final Map<Comparable, Entry> table, final SortedSet<Comparable> sorted, final DynamicContext dynEnv, final Type type) {
                super(dynEnv);
                if(sorted == null) {
                    throw new IllegalArgumentException();
                }
                this._table = table;
                this._sortedKeys = sorted;
                this._type = type;
            }

            @Override
            public Focus<Item> iterator() {
                final Map<Comparable, Entry> tbl = _table;
                final int keylen = _sortedKeys.size();
                final ArrayList<Item> items = new ArrayList<Item>(keylen * 2);
                for(Comparable key : _sortedKeys) {
                    final Entry entry = tbl.get(key);
                    final Item[] ei = entry.getItems();
                    for(Item i : ei) {
                        items.add(i);
                    }
                }
                final Focus<Item> focus = new Focus<Item>(this, items.iterator(), _dynEnv);
                return focus;
            }

            public boolean next(IFocus focus) throws XQueryException {
                final Iterator<Item> itemItor = focus.getBaseFocus();
                if(itemItor.hasNext()) {
                    final Item curItem = itemItor.next();
                    focus.setContextItem(curItem);
                    return true;
                }
                return false;
            }

            public Type getType() {
                return _type;
            }

        }

        private Sequence<Item> entrySequence(Comparable key, DynamicContext dynEnv) {
            if(_equiJoin) { // ==
                final Entry entry = find(key);
                if(entry == null) {
                    return ValueSequence.EMPTY_SEQUENCE;
                }
                Item[] items = entry.getItems();
                return new ValueSequence(Arrays.asList(items), dynEnv);
            }
            assert (_orderedKey != null);
            final SortedSet<Comparable> sorted;
            if(_opr.indexOf('<') != -1) { // <
                SortedSet<Comparable> heads = _orderedKey.headSet(key); // note that headSet contains key itself.
                if(_opr.indexOf('=') == -1) {
                    heads.remove(key);
                }
                sorted = heads;
            } else if(_opr.indexOf('>') != -1) { // >
                SortedSet<Comparable> tails = _orderedKey.tailSet(key); // note that tailSet doesn't contain key itself.
                if(_opr.indexOf('=') != -1) {
                    tails.add(key);
                }
                sorted = tails;
            } else if(NE.sgn.equals(_opr)) { // !=
                SortedSet<Comparable> excepts = new TreeSet<Comparable>(_orderedKey);
                excepts.remove(key);
                sorted = excepts;
            } else {
                throw new IllegalStateException("Illegal operator: " + _opr);
            }
            if(sorted.isEmpty()) {
                return ValueSequence.EMPTY_SEQUENCE;
            }
            return new JoinEntrySequence(_table, sorted, dynEnv, _type);
        }

        private Entry find(Comparable key) {
            return _table.get(key);
        }

        private void put(Comparable[] keys, Item value) {
            assert (keys != null);
            assert (keys.length > 0);
            final Comparable key;
            if(keys.length == 1) {
                key = keys[0];
                assert (key != null);
            } else {
                _probe.keys = keys;
                key = _probe;
            }
            Entry entry = _table.get(key);
            if(entry != null) {
                entry.add(value);
            } else {
                entry = new Entry(value);
                Comparable k = (key == _probe) ? new Key(keys, _opr) : key;
                _table.put(k, entry);
                if(!_equiJoin) {
                    _orderedKey.add(k);
                }
            }
        }
    }

}
TOP

Related Classes of xbird.xquery.expr.opt.Join

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.