Package xbird.xquery.misc

Source Code of xbird.xquery.misc.QNameTable$QualifiedName

/*
* @(#)$Id:QNameTable.java 2335 2007-07-17 04:14:15Z 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.misc;

import java.io.Externalizable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;

import javax.xml.XMLConstants;
import javax.xml.namespace.QName;

import xbird.storage.DbCollection;
import xbird.storage.DbException;
import xbird.util.concurrent.reference.ReferenceMap;
import xbird.util.concurrent.reference.ReferenceType;
import xbird.util.string.StringUtils;
import xbird.util.xml.XMLUtils;
import xbird.xquery.XQRTException;
import xbird.xquery.expr.path.NodeTest;

/**
*
* <DIV lang="en"></DIV>
* <DIV lang="ja"></DIV>
*
* @author Makoto YUI (yuin405+xbird@gmail.com)
*/
public final class QNameTable implements Externalizable {
    private static final long serialVersionUID = 1L;

    private static final int FLAG_BITS = 1;
    private static final int HAS_PREFIX_FLAG = FLAG_BITS << 31;
    private static final int FLAP_PREFIX = HAS_PREFIX_FLAG - 1;
    private static final int PREFIX_BITS = 4; // prefix is resctricted to 16 per QName
    private static final int NAME_BITS = 32 - FLAG_BITS - PREFIX_BITS; // 27
    private static final int MAX_NAMES = 1 << NAME_BITS; // resctricted to 2^27
    private static final int NAME_MASK = MAX_NAMES - 1;
    private static final int PREFIX_SHIFT = NAME_BITS;

    private static final Map<QualifiedName, QualifiedName> __pendings = new ReferenceMap<QualifiedName, QualifiedName>(ReferenceType.STRONG, ReferenceType.SOFT, 64);
    private final QualifiedName probe = new QualifiedName("", "");
    private boolean _dirty = true;

    //--------------------------------------------
    // persistent stuff

    private Map<QualifiedName, QualifiedName> nameMap;
    private List<QualifiedName> orderedNames;
    private int totalOrderedNames = 0;
    private int estimateSize = 0;
    private List<String> prefixes = new ArrayList<String>(24);

    //--------------------------------------------

    public QNameTable() {}

    public QNameTable(int initialCapacity) {
        this.nameMap = new HashMap<QualifiedName, QualifiedName>(initialCapacity, 0.7f);
        this.orderedNames = new ArrayList<QualifiedName>(initialCapacity);
    }

    public void load(QNameTable symbols) {
        final Collection<QualifiedName> names = symbols.orderedNames;
        for(QualifiedName qn : names) {
            regist(qn);
        }
    }

    public void setDirty(final boolean flag) {
        this._dirty = flag;
    }

    public QualifiedName find(QualifiedName qname) {
        assert (qname != null);
        return find(qname.getNamespaceURI(), qname.getLocalPart());
    }

    public QualifiedName find(String nsuri, String name) {
        probe.nsuri = (nsuri == null) ? XMLUtils.NULL_NS_URI : nsuri;
        assert (name != null);
        probe.localName = name;
        return nameMap.get(probe);
    }

    @Deprecated
    public boolean remove(QualifiedName qname) {
        String nsuri = qname.getNamespaceURI();
        probe.nsuri = (nsuri == null) ? XMLUtils.NULL_NS_URI : nsuri;
        probe.localName = qname.getLocalPart();
        QualifiedName removed = nameMap.remove(probe);
        return qname == removed;
    }

    @Deprecated
    public boolean contains(QualifiedName qname) {
        String nsuri = qname.getNamespaceURI();
        probe.nsuri = (nsuri == null) ? XMLUtils.NULL_NS_URI : nsuri;
        probe.localName = qname.getLocalPart();
        return nameMap.containsKey(probe);
    }

    public int regist(QualifiedName qname) {
        final String nsuri = qname.getNamespaceURI();
        final String lpart = qname.getLocalPart();
        assert (lpart != null);
        return regist((nsuri == null) ? XMLUtils.NULL_NS_URI : nsuri, lpart);
    }

    public int regist(String nsuri, String name) {
        probe.nsuri = (nsuri == null) ? XMLUtils.NULL_NS_URI : nsuri;
        assert (name != null);
        probe.localName = name;
        final QualifiedName predefined = __pendings.remove(probe);
        if(predefined != null) {
            assert (predefined.id == -1);
            predefined.id = nameMap.size();
            final int eid = put(predefined);
            return eid;
        }
        QualifiedName entry = nameMap.get(probe);
        if(entry == null) {
            entry = new QualifiedName(nsuri, name);
            entry.id = nameMap.size();
            final int eid = put(entry);
            return eid;
        } else {
            return entry.id;
        }
    }

    public int regist(String nsuri, String localName, String prefix) {
        final int nameId = regist(nsuri, localName);
        return bindPrefix(nameId, prefix);
    }

    private int put(QualifiedName entry) {
        assert (entry != null);
        entry.id = nameMap.size();
        assert (entry.id <= MAX_NAMES);
        QualifiedName replaced = nameMap.put(entry, entry);
        assert (replaced == null);
        orderedNames.add(entry);
        ++totalOrderedNames;
        estimateSize += (2 * (entry.getNamespaceURI().length() + entry.getLocalPart().length())) + 4;
        return entry.id;
    }

    private int bindPrefix(int nameId, final String prefix) {
        assert (prefix != null);
        if(prefix.length() == 0) {
            return nameId;
        }
        int prefixCode = prefixes.indexOf(prefix);
        if(prefixCode != -1 && (nameId & HAS_PREFIX_FLAG) != 0) {
            if(prefixCodeEquals(nameId, prefixCode)) {
                return nameId;
            }
            QualifiedName allocated = getName(nameId);
            QualifiedName newName = new QualifiedName(allocated.nsuri, allocated.localName, prefix);
            nameId = nameMap.size();
            QualifiedName replaced = nameMap.put(newName, newName);
            assert (replaced == null);
            orderedNames.add(newName);
            ++totalOrderedNames;
        } else {
            QualifiedName qname = getName(nameId);
            qname.prefix = prefix;
        }
        assert ((nameId & HAS_PREFIX_FLAG) == 0) : "should not have prefix flag: "
                + StringUtils.toBitString(nameId);
        int code = nameId | HAS_PREFIX_FLAG;
        if(prefixCode == -1) {
            prefixCode = prefixes.size();
            prefixes.add(prefix);
        }
        estimateSize += (2 * prefix.length());
        return code | (prefixCode << PREFIX_SHIFT);
    }

    private static boolean prefixCodeEquals(int nameId, int prefixCode) {
        assert ((nameId & HAS_PREFIX_FLAG) != 0);
        final int pindex = (nameId & FLAP_PREFIX) >> PREFIX_SHIFT;
        return pindex == prefixCode;
    }

    public static boolean nameEquals(final int myNameCode, final int nameCode) {
        if((myNameCode & HAS_PREFIX_FLAG) != 0 || (nameCode & HAS_PREFIX_FLAG) != 0) {
            return (NAME_MASK & myNameCode) == (NAME_MASK & nameCode);
        } else {
            return myNameCode == nameCode;
        }
    }

    public QualifiedName getName(int at) {
        final int addr;
        if((at & HAS_PREFIX_FLAG) != 0) {
            addr = (at & NAME_MASK);
        } else {
            addr = at;
        }
        final int last = totalOrderedNames - 1;
        if(at > last) {
            throw new NoSuchElementException("element at #" + at + " not found, last=" + last + '.');
        }
        return orderedNames.get(addr);
    }

    public void clear() {
        nameMap.clear();
    }

    public int estimateMemorySize() {
        return estimateSize;
    }

    public String getPrefix(int id) {
        if((id & HAS_PREFIX_FLAG) == 0) {
            return "";
        }
        return prefixes.get(getPrefixCode(id));
    }

    public int getPrefixCode(int id) {
        assert ((id & HAS_PREFIX_FLAG) != 0);
        final int pindex = (id & FLAP_PREFIX) >> PREFIX_SHIFT;
        assert (pindex <= (1 << PREFIX_BITS));
        assert (pindex < prefixes.size());
        return pindex;
    }

    public static QualifiedName instantiate(String nsuri, String localName, String prefix) {
        final QualifiedName name = new QualifiedName(nsuri, localName, prefix);
        __pendings.put(name, name);
        return name;
    }

    public static QualifiedName instantiate(String nsuri, String localName) {
        return instantiate(nsuri, localName, XMLConstants.DEFAULT_NS_PREFIX);
    }

    public static final class QualifiedName implements Externalizable {
        private static final long serialVersionUID = 670169881289325639L;

        private int id = -1;
        private String prefix = "";
        private String nsuri, localName;

        public QualifiedName() {}

        private QualifiedName(String nsuri, String localName, String prefix) {
            assert (nsuri != null && localName != null);
            assert (prefix != null);
            if(localName.length() != 0 && !NodeTest.ANY.equals(localName)
                    && !XMLUtils.isNCName(localName)) {
                throw new XQRTException("err:XPST0081", "Illegally specified a non-ncname string as name: "
                        + localName);
            }
            if("*".equals(prefix)) {
                this.nsuri = "*";// hack
            } else {
                this.nsuri = nsuri.intern();
            }
            this.localName = localName.intern();
            this.prefix = prefix;
        }

        private QualifiedName(String nsuri, String localName) {
            this(nsuri, localName, XMLConstants.DEFAULT_NS_PREFIX);
        }

        @Override
        public int hashCode() {
            return nsuri.hashCode() ^ localName.hashCode();
        }

        @Override
        public boolean equals(Object obj) {
            if(obj == this) {
                return true;
            }
            if(obj == null || !(obj instanceof QualifiedName)) {
                return false;
            }
            final QualifiedName trg = (QualifiedName) obj;
            if(id != -1 && trg.id != -1) {
                if((id & HAS_PREFIX_FLAG) != 0 || (trg.id & HAS_PREFIX_FLAG) != 0) {
                    return (NAME_MASK & id) == (NAME_MASK & trg.id);
                } else {
                    return id == trg.id;
                }
            } else {
                return nsuri.equals(trg.getNamespaceURI()) && localName.equals(trg.getLocalPart());
            }
        }

        public int identity() {
            return id;
        }

        public String getNamespaceURI() {
            return nsuri;
        }

        public String getLocalPart() {
            return localName;
        }

        public String getPrefix() {
            return prefix;
        }

        @Override
        public String toString() {
            if(nsuri.length() == 0) {
                return localName;
            } else {
                return '{' + nsuri + '}' + localName;
            }
        }

        public void writeExternal(ObjectOutput out) throws IOException {
            out.writeInt(id);
            out.writeUTF(prefix);
            out.writeUTF(nsuri);
            out.writeUTF(localName);
        }

        public void readExternal(ObjectInput in) throws IOException {
            this.id = in.readInt();
            this.prefix = in.readUTF();
            this.nsuri = in.readUTF().intern();
            this.localName = in.readUTF().intern();
        }

        public static QualifiedName readFrom(final ObjectInput in) throws IOException {
            final QualifiedName qname = new QualifiedName();
            qname.readExternal(in);
            return qname;
        }

        public static QName toJavaxQName(final QualifiedName qname) {
            if(qname == null) {
                return null;
            }
            return new QName(qname.nsuri, qname.localName, qname.prefix);
        }

    }

    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(nameMap);
        out.writeObject(orderedNames);
        out.writeInt(estimateSize);
        out.writeObject(prefixes);
    }

    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        this.nameMap = (Map<QualifiedName, QualifiedName>) in.readObject();
        this.orderedNames = (List<QualifiedName>) in.readObject();
        this.totalOrderedNames = orderedNames.size();
        this.estimateSize = in.readInt();
        this.prefixes = (List<String>) in.readObject();
    }

    public static QNameTable read(ObjectInput in) throws IOException, ClassNotFoundException {
        QNameTable tbl = new QNameTable();
        tbl.readExternal(in);
        return tbl;
    }

    public synchronized void flush(DbCollection col) throws DbException {
        if(!_dirty) {
            return;
        }
        File file = new File(col.getDirectory(), col.getCollectionName()
                + DbCollection.QNAMES_FILE_SUFFIX);
        if(file.exists()) {
            if(!file.delete()) {
                throw new DbException("Could not delete symbol file: " + file.getAbsolutePath());
            }
        }
        if(file.canWrite()) {
            throw new IllegalStateException("Does not have write permission for "
                    + file.getAbsolutePath());
        }
        try {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
            oos.writeObject(this);
            oos.flush();
            oos.close();
        } catch (FileNotFoundException fe) {
            throw new DbException(fe);
        } catch (IOException ioe) {
            throw new DbException(ioe);
        }
    }

}
TOP

Related Classes of xbird.xquery.misc.QNameTable$QualifiedName

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.