Package xbird.xquery.dm.value.sequence

Source Code of xbird.xquery.dm.value.sequence.GroupedSequence$GroupKeys

/*
* @(#)$Id: codetemplate_xbird.xml 3792 2008-04-21 21:39:23Z 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.dm.value.sequence;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.Map.Entry;

import xbird.util.collections.IdentityHashSet;
import xbird.xquery.DynamicError;
import xbird.xquery.XQRTException;
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.expr.XQExpression;
import xbird.xquery.expr.flwr.ForClause;
import xbird.xquery.expr.flwr.GroupingSpec;
import xbird.xquery.expr.flwr.LetClause;
import xbird.xquery.expr.var.BindingVariable;
import xbird.xquery.expr.var.VarRef;
import xbird.xquery.expr.var.Variable;
import xbird.xquery.expr.var.BindingVariable.ForVariable;
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.XQueryContext;
import xbird.xquery.parser.visitor.AbstractXQueryParserVisitor;
import xbird.xquery.type.Type;

/**
*
* <DIV lang="en"></DIV>
* <DIV lang="ja"></DIV>
*
* @author Makoto YUI (yuin405+xbird@gmail.com)
* @link http://www.w3.org/TR/2008/WD-xquery-11-20080711/#id-group-by
*/
public final class GroupedSequence extends AbstractSequence {
    private static final long serialVersionUID = 3549494228786882066L;

    private final Map<GroupKeys, GroupEntry> _map;
    private Shuffler _shuffler;

    private final Type _type;

    public GroupedSequence(Sequence src, GroupingSpec[] specs, List<BindingVariable> nonGroupingVariables, Sequence contextSeq, DynamicContext dynEnv) {
        this(src, specs, nonGroupingVariables, contextSeq, dynEnv, false);
    }

    public GroupedSequence(Sequence src, GroupingSpec[] specs, List<BindingVariable> nonGroupingVariables, Sequence contextSeq, DynamicContext dynEnv, boolean sort) {
        super(dynEnv);
        if(specs.length < 1) {
            throw new IllegalArgumentException("No group keys was specified.");
        }
        if(sort || specs.length > 1) {
            //this._map = new ConcurrentSkipListMap<Item[], Sequence>();    // more memory spaces are required than TreeMap.
            this._map = new TreeMap<GroupKeys, GroupEntry>();
        } else {
            this._map = new HashMap<GroupKeys, GroupEntry>(256);
        }
        this._shuffler = new Shuffler(_map, src, specs, nonGroupingVariables, contextSeq, dynEnv);
        this._type = src.getType();
    }

    public Type getType() {
        return _type;
    }

    @Override
    public Focus iterator() {
        if(_shuffler != null) {
            try {
                _shuffler.shuffle();
            } catch (XQueryException e) {
                throw new XQRTException("Failed grouping", e);
            }
            this._shuffler = null;
        }
        GroupingIterator itor = new GroupingIterator(_map, _dynEnv);
        return new Focus(this, itor, _dynEnv);
    }

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

    private static final class Shuffler implements Serializable {
        private static final long serialVersionUID = 8499075321609368166L;

        final Map<GroupKeys, GroupEntry> map;

        final Sequence src;
        final GroupingSpec[] specs;
        final List<BindingVariable> nonGroupingVariables;
        final Sequence contextSeq;
        final DynamicContext dynEnv;

        Shuffler(Map<GroupKeys, GroupEntry> map, Sequence src, GroupingSpec[] specs, List<BindingVariable> nonGroupingVariables, Sequence contextSeq, DynamicContext dynEnv) {
            this.map = map;
            this.src = src;
            this.specs = specs;
            this.nonGroupingVariables = nonGroupingVariables;
            this.contextSeq = contextSeq;
            this.dynEnv = dynEnv;
        }

        public void shuffle() throws XQueryException {
            final IFocus<? extends Item> itor = src.iterator();
            for(Item it : itor) {//TODO parallel map
                final int len = specs.length;
                final Item[] groupKeys = new Item[len];
                final Sequence[] rawKeys = new Sequence[len];
                for(int i = 0; i < len; i++) {
                    GroupingSpec spec = specs[i];
                    Sequence result = spec.getKeyExpr().eval(contextSeq, dynEnv);
                    Sequence atomized = result.atomize(dynEnv);
                    IFocus atomizedItor = atomized.iterator();

                    final Item groupKey;
                    if(atomizedItor.hasNext()) {
                        AtomicValue atom = (AtomicValue) atomizedItor.next();
                        if(atomizedItor.hasNext()) {
                            atomizedItor.closeQuietly();
                            throw new DynamicError("err:XQDY0095", "Illegal resulting value for a grouping variable: "
                                    + atomized);
                        }
                        groupKey = atom.asGroupingValue();
                    } else {
                        groupKey = ValueSequence.EMPTY_SEQUENCE;
                    }
                    atomizedItor.closeQuietly();

                    groupKeys[i] = groupKey;
                    rawKeys[i] = result;
                }

                final GroupKeys k = new GroupKeys(specs, groupKeys, rawKeys, dynEnv);
                final GroupEntry v = new GroupEntry(it, dynEnv);

                final GroupEntry prevEntry = map.put(k, v);
                if(prevEntry != null) {
                    v.setNonGroupingVaribales(prevEntry.getNonGroupingVaribales());

                    final Sequence prevValue = prevEntry.getValue();
                    if(prevValue instanceof ValueSequence) {
                        ((ValueSequence) prevValue).addItem(it);
                        v.setValue(prevValue);
                    } else {
                        final List<Item> list = new ArrayList<Item>(4);
                        list.add(SingleCollection.wrap(prevValue, dynEnv));
                        list.add(it);
                        ValueSequence newSeq = new ValueSequence(list, dynEnv);
                        v.setValue(newSeq);
                    }
                }

                for(BindingVariable ngv : nonGroupingVariables) {
                    v.putNonGroupingVariable(ngv);
                }
            }
            itor.closeQuietly();
        }
    }

    private static final class GroupEntry implements Externalizable {
        private static final long serialVersionUID = 8594237358142781529L;

        Sequence value;
        Map<BindingVariable, Sequence> nonGroupingVaribales = null;

        transient final DynamicContext dynEnv;

        public GroupEntry() {//Externalizable
            this.dynEnv = DynamicContext.DUMMY;
        }

        public GroupEntry(Sequence value, DynamicContext dynEnv) {
            this.value = value;
            this.dynEnv = dynEnv;
        }

        public Sequence getValue() {
            return value;
        }

        public void setValue(Sequence newValue) {
            this.value = newValue;
        }

        public Map<BindingVariable, Sequence> getNonGroupingVaribales() {
            return nonGroupingVaribales;
        }

        public void setNonGroupingVaribales(Map<BindingVariable, Sequence> nonGroupingVaribales) {
            this.nonGroupingVaribales = nonGroupingVaribales;
        }

        public void putNonGroupingVariable(BindingVariable var) throws DynamicError {
            final Sequence result = var.getResult();
            if(result == null) {
                throw new DynamicError("Result is not bounded for " + var);
            }
            if(nonGroupingVaribales == null) {
                this.nonGroupingVaribales = new IdentityHashMap<BindingVariable, Sequence>(4);
                nonGroupingVaribales.put(var, result);
            } else {
                final Sequence prevValue = nonGroupingVaribales.get(var);
                if(prevValue == null) {
                    nonGroupingVaribales.put(var, result);
                } else {
                    if(prevValue instanceof ValueSequence) {
                        Item resultItem = SingleCollection.wrap(result, dynEnv);
                        ((ValueSequence) prevValue).addItem(resultItem);
                    } else {
                        final List<Item> list = new ArrayList<Item>(4);
                        list.add(SingleCollection.wrap(prevValue, dynEnv));
                        list.add(SingleCollection.wrap(result, dynEnv));
                        ValueSequence newSeq = new ValueSequence(list, dynEnv);
                        nonGroupingVaribales.put(var, newSeq);
                    }
                }
            }
        }

        public void assignNonGroupingVariables() {
            if(nonGroupingVaribales != null && !nonGroupingVaribales.isEmpty()) {
                for(Entry<BindingVariable, Sequence> entry : nonGroupingVaribales.entrySet()) {
                    BindingVariable var = entry.getKey();
                    Sequence value = entry.getValue();
                    var.allocateResult(value, dynEnv);
                }
            }
        }

        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
            this.value = (Sequence) in.readObject();
            this.nonGroupingVaribales = (Map<BindingVariable, Sequence>) in.readObject();
        }

        public void writeExternal(ObjectOutput out) throws IOException {
            out.writeObject(value);
            out.writeObject(nonGroupingVaribales);
        }

    }

    private static final class GroupKeys implements Comparable<GroupKeys>, Externalizable {
        private static final long serialVersionUID = 665438608513377668L;

        transient/* final */GroupingSpec[] _specs;
        transient/* final */Item[] _keys;
        transient/* final */Sequence[] _rawKeys;

        transient final DynamicContext _dynEnv;

        public GroupKeys() {// for Externalizable
            this._dynEnv = DynamicContext.DUMMY;
        }

        public GroupKeys(GroupingSpec[] specs, Item[] keys, Sequence[] rawKeys, DynamicContext dynEnv) {
            assert (specs.length == keys.length);
            this._specs = specs;
            this._keys = keys;
            this._rawKeys = rawKeys;
            this._dynEnv = dynEnv;
        }

        public void assignGroupingKeys() {
            final int len = _specs.length;
            for(int i = 0; i < len; i++) {
                BindingVariable groupingVar = _specs[i].getKeyExpr();
                groupingVar.allocateResult(_rawKeys[i], _dynEnv);
            }
        }

        public int compareTo(GroupKeys other) {
            if(this == other) {
                return 0;
            }
            final int keylen = _keys.length;
            final Item[] otherKeys = other._keys;
            assert (keylen == otherKeys.length);
            for(int i = 0; i < keylen; i++) {
                final int cmp = _specs[i].compare(_keys[i], otherKeys[i], _dynEnv);
                if(cmp != 0) {
                    return cmp;
                }
            }
            return 0;
        }

        @Override
        public boolean equals(Object obj) {
            if(obj == this) {
                return true;
            }
            if(obj instanceof GroupKeys) {
                return Arrays.equals(_keys, ((GroupKeys) obj)._keys);
            }
            return false;
        }

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

        @Override
        public String toString() {
            return Arrays.toString(_keys);
        }

        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
            final int len = in.readInt();
            final GroupingSpec[] specs = new GroupingSpec[len];
            final Item[] keys = new Item[len];
            final Sequence[] rawKeys = new Sequence[len];
            for(int i = 0; i < len; i++) {
                specs[i] = (GroupingSpec) in.readObject();
                keys[i] = (Item) in.readObject();
                rawKeys[i] = (Sequence) in.readObject();
            }
            this._specs = specs;
            this._keys = keys;
            this._rawKeys = rawKeys;
        }

        public void writeExternal(ObjectOutput out) throws IOException {
            final int len = _specs.length;
            out.writeInt(len);
            for(int i = 0; i < len; i++) {
                out.writeObject(_specs[i]);
                out.writeObject(_keys[i]);
                out.writeObject(_rawKeys[i]);
            }
        }
    }

    private static final class GroupingIterator implements Iterator<Item>, Serializable {
        private static final long serialVersionUID = 3766846173804574765L;

        final Map<GroupKeys, GroupEntry> _map;
        final DynamicContext _dynEnv;

        transient Iterator<Entry<GroupKeys, GroupEntry>> _itor;

        public GroupingIterator(Map<GroupKeys, GroupEntry> map, DynamicContext dynEnv) {
            this._map = map;
            this._dynEnv = dynEnv;
            this._itor = map.entrySet().iterator();
        }

        public boolean hasNext() {
            return _itor.hasNext();
        }

        public Item next() {
            Entry<GroupKeys, GroupEntry> entry = _itor.next();
            entry.getKey().assignGroupingKeys();
            GroupEntry value = entry.getValue();
            value.assignNonGroupingVariables();
            Sequence postGroupingSeq = value.getValue();
            return SingleCollection.wrap(postGroupingSeq, _dynEnv);
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }

        private Object readResolve() throws ObjectStreamException {
            assert (_map != null);
            this._itor = _map.entrySet().iterator();
            return this;
        }
    }

    public static final class PreGroupingVariableExtractor extends AbstractXQueryParserVisitor {

        private final List<BindingVariable> variablesHolder;
        private final Set<Variable> excludedVariableList;

        public PreGroupingVariableExtractor(GroupingSpec[] specs) {
            this.variablesHolder = new ArrayList<BindingVariable>(4);
            this.excludedVariableList = new IdentityHashSet<Variable>(8);
            for(GroupingSpec spec : specs) {
                BindingVariable var = spec.getKeyExpr();
                excludedVariableList.add(var);
            }
        }

        public List<BindingVariable> getNonGroupingVariables() {
            return variablesHolder;
        }

        @Override
        public ForClause visit(ForClause clause, XQueryContext ctxt) throws XQueryException {
            ForVariable var = clause.getVariable();
            excludedVariableList.add(var);
            XQExpression e = var.getValue();
            e.visit(this, ctxt);
            return clause;
        }

        @Override
        public LetClause visit(LetClause clause, XQueryContext ctxt) throws XQueryException {
            LetVariable var = clause.getVariable();
            excludedVariableList.add(var);
            XQExpression e = var.getValue();
            e.visit(this, ctxt);
            return clause;
        }

        @Override
        public XQExpression visit(VarRef ref, XQueryContext ctxt) throws XQueryException {
            final Variable var = ref.getValue();
            if(var instanceof BindingVariable) {
                if(!excludedVariableList.contains(var)) {
                    variablesHolder.add((BindingVariable) var);
                }
            }
            return ref;
        }

    }

}
TOP

Related Classes of xbird.xquery.dm.value.sequence.GroupedSequence$GroupKeys

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.