You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2017/09/28 11:08:46 UTC
[30/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis
as subdirectory jena-db/
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableOps.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableOps.java
index 0000000,0000000..378bdfc
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableOps.java
@@@ -1,0 -1,0 +1,50 @@@
++/*
++ * 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.
++ *
++ * See the NOTICE file distributed with this work for additional
++ * information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetable;
++
++import java.util.ArrayList ;
++import java.util.List ;
++
++import org.apache.jena.graph.Node ;
++import org.seaborne.tdb2.store.NodeId ;
++
++public class NodeTableOps {
++
++ /** Convert a bulk operation into a loop */
++ public static List<NodeId> bulkNodeToNodeIdImpl(NodeTable nt, List<Node> nodes, boolean withAllocation) {
++ List<NodeId> nodeIds = new ArrayList<>(nodes.size()) ;
++ for ( Node node : nodes ) {
++ NodeId nid = withAllocation ? nt.getAllocateNodeId(node) : nt.getNodeIdForNode(node) ;
++ nodeIds.add(nid) ;
++ }
++ return nodeIds ;
++ }
++
++ /** Convert a bulk operation into a loop */
++ public static List<Node> bulkNodeIdToNodeImpl(NodeTable nt, List<NodeId> nodeIds) {
++ List<Node> nodes = new ArrayList<>(nodeIds.size()) ;
++ for ( NodeId nodeId : nodeIds ) {
++ Node n = nt.getNodeForNodeId(nodeId) ;
++ nodes.add(n) ;
++ }
++ return nodes ;
++ }
++
++
++}
++
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableTRDF.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableTRDF.java
index 0000000,0000000..44f71d4
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableTRDF.java
@@@ -1,0 -1,0 +1,108 @@@
++/*
++ * 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.
++ *
++ * See the NOTICE file distributed with this work for additional
++ * information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetable ;
++
++import org.apache.jena.atlas.logging.Log ;
++import org.apache.jena.graph.Node ;
++import org.apache.jena.riot.thrift.RiotThriftException ;
++import org.apache.jena.riot.thrift.TRDF ;
++import org.apache.jena.riot.thrift.ThriftConvert ;
++import org.apache.jena.riot.thrift.wire.RDF_Term ;
++import org.apache.thrift.TException ;
++import org.apache.thrift.protocol.TProtocol ;
++import org.seaborne.dboe.base.file.BinaryDataFile ;
++import org.seaborne.dboe.index.Index ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.store.NodeIdFactory;
++
++/** NodeTable using Thrift for the I/O and storage. */
++
++public class NodeTableTRDF extends NodeTableNative {
++ // Write buffering is done in the underlying BinaryDataFile
++ BinaryDataFile diskFile ;
++ private TReadAppendFileTransport transport ;
++ private final TProtocol protocol ;
++
++ public NodeTableTRDF(Index nodeToId, BinaryDataFile objectFile) {
++ super(nodeToId) ;
++ try {
++ this.diskFile = objectFile ;
++ transport = new TReadAppendFileTransport(diskFile) ;
++ if ( ! transport.isOpen() )
++ transport.open();
++ this.protocol = TRDF.protocol(transport) ;
++ }
++ catch (Exception ex) {
++ throw new TDBException("NodeTableTRDF", ex) ;
++ }
++ }
++
++ @Override
++ protected NodeId writeNodeToTable(Node node) {
++ RDF_Term term = ThriftConvert.convert(node, true) ;
++ try {
++ long x = diskFile.length() ;
++ // Paired : [*]
++ NodeId nid = NodeIdFactory.createPtr(x);
++ term.write(protocol) ;
++ //transport.flush() ;
++ return nid ;
++ }
++ catch (Exception ex) {
++ throw new TDBException("NodeTableThrift/Write", ex) ;
++ }
++ }
++
++ @Override
++ protected Node readNodeFromTable(NodeId id) {
++ try {
++ // Paired : [*]
++ long x = id.getPtrLocation();
++ transport.readPosition(x) ;
++ RDF_Term term = new RDF_Term() ;
++ term.read(protocol) ;
++ Node n = ThriftConvert.convert(term) ;
++ return n ;
++ }
++ catch (TException ex) {
++ throw new TDBException("NodeTableTRDF/Read", ex) ;
++ }
++ catch (RiotThriftException ex) {
++ Log.error(this, "Bad encoding: NodeId = "+id) ;
++ throw ex ;
++ }
++ }
++
++ @Override
++ protected void syncSub() {
++ try { transport.flush(); }
++ catch (Exception ex) { throw new TDBException("NodeTableTRDF", ex) ; }
++ }
++
++ @Override
++ protected void closeSub() {
++ if ( transport.isOpen() ) {
++ try { transport.close() ; }
++ catch (Exception ex) { throw new TDBException("NodeTableTRDF", ex) ; }
++ }
++ }
++
++ public Index getIndex() { return nodeHashToId ; }
++ public BinaryDataFile getData() { return diskFile ; }
++}
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableWrapper.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableWrapper.java
index 0000000,0000000..f18203d
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableWrapper.java
@@@ -1,0 -1,0 +1,93 @@@
++/*
++ * 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.
++ *
++ * See the NOTICE file distributed with this work for additional
++ * information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetable ;
++
++import java.util.Iterator ;
++import java.util.List ;
++
++import org.apache.jena.atlas.lib.Pair ;
++import org.apache.jena.graph.Node ;
++import org.seaborne.tdb2.store.NodeId ;
++
++public class NodeTableWrapper implements NodeTable {
++ protected final NodeTable nodeTable ;
++
++ @Override
++ public final NodeTable wrapped() {
++ return nodeTable ;
++ }
++
++ protected NodeTableWrapper(NodeTable nodeTable) {
++ this.nodeTable = nodeTable ;
++ }
++
++ @Override
++ public NodeId getAllocateNodeId(Node node) {
++ return nodeTable.getAllocateNodeId(node) ;
++ }
++
++ @Override
++ public NodeId getNodeIdForNode(Node node) {
++ return nodeTable.getNodeIdForNode(node) ;
++ }
++
++ @Override
++ public Node getNodeForNodeId(NodeId id) {
++ return nodeTable.getNodeForNodeId(id) ;
++ }
++
++ @Override
++ public boolean containsNode(Node node) {
++ return nodeTable.containsNode(node) ;
++ }
++
++ @Override
++ public boolean containsNodeId(NodeId nodeId) {
++ return nodeTable.containsNodeId(nodeId) ;
++ }
++
++ @Override
++ public List<NodeId> bulkNodeToNodeId(List<Node> nodes, boolean withAllocation) {
++ return nodeTable.bulkNodeToNodeId(nodes, withAllocation) ;
++ }
++
++ @Override
++ public List<Node> bulkNodeIdToNode(List<NodeId> nodeIds) {
++ return nodeTable.bulkNodeIdToNode(nodeIds) ;
++ }
++
++ @Override
++ public Iterator<Pair<NodeId, Node>> all() {
++ return nodeTable.all() ;
++ }
++
++ @Override
++ public boolean isEmpty() {
++ return nodeTable.isEmpty() ;
++ }
++
++ @Override
++ public void sync() {
++ nodeTable.sync() ;
++ }
++
++ @Override
++ public void close() {
++ nodeTable.close() ;
++ }
++}
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/TReadAppendFileTransport.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/TReadAppendFileTransport.java
index 0000000,0000000..34eebfb
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/TReadAppendFileTransport.java
@@@ -1,0 -1,0 +1,85 @@@
++/*
++ * 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.
++ *
++ * See the NOTICE file distributed with this work for additional
++ * information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetable;
++
++import java.util.Objects ;
++
++import org.apache.thrift.transport.TTransport ;
++import org.seaborne.dboe.base.file.BinaryDataFile ;
++
++/** A file transport that supports random access read and
++ * buffered append write.
++ * <p>
++ * Adapter TTransport -> BinaryDataFile
++ */
++public class TReadAppendFileTransport extends TTransport {
++ private BinaryDataFile file ;
++ private long readPosn = -1 ;
++
++ public TReadAppendFileTransport(BinaryDataFile file) {
++ Objects.requireNonNull(file) ;
++ this.file = file ;
++ }
++
++ @Override
++ public boolean isOpen() {
++ return file.isOpen() ;
++ }
++
++ @Override
++ public void open() {
++ file.open() ;
++ }
++
++ @Override
++ public void close() {
++ file.close() ;
++ }
++
++ public void truncate(long posn) {
++ file.truncate(posn);
++ }
++
++ public BinaryDataFile getBinaryDataFile() { return file ; }
++
++ public long readPosition() {
++ return readPosn ;
++ }
++
++ public void readPosition(long posn) {
++ readPosn = posn ;
++ }
++
++ @Override
++ public int read(byte[] buf, int off, int len) {
++ int x = file.read(readPosn, buf, off, len) ;
++ readPosn += x ;
++ return x ;
++ }
++
++ @Override
++ public void write(byte[] buf, int off, int len) {
++ file.write(buf, off, len) ;
++ }
++
++ @Override
++ public void flush() {
++ file.sync();
++ }
++}
++
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTable.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTable.java
index 0000000,0000000..4d48189
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTable.java
@@@ -1,0 -1,0 +1,71 @@@
++/*
++ * 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.
++ *
++ * See the NOTICE file distributed with this work for additional
++ * information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetupletable ;
++
++import java.util.Iterator ;
++
++import org.apache.jena.atlas.lib.Closeable ;
++import org.apache.jena.atlas.lib.Sync ;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.graph.Node ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.store.nodetable.NodeTable ;
++import org.seaborne.tdb2.store.tupletable.TupleTable ;
++
++public interface NodeTupleTable extends Sync, Closeable
++{
++ public void addRow(Node... nodes) ;
++
++ public void deleteRow(Node... nodes) ;
++
++ /** Find by node. */
++ public Iterator<Tuple<Node>> find(Node... nodes) ;
++
++ /** Find by node - return an iterator of NodeIds. Can return "null" for not found as well as NullIterator */
++ public Iterator<Tuple<NodeId>> findAsNodeIds(Node... nodes) ;
++
++ /** Find by NodeId. */
++ public Iterator<Tuple<NodeId>> find(NodeId... ids) ;
++
++ /** Find by NodeId. */
++ public Iterator<Tuple<NodeId>> find(Tuple<NodeId> ids) ;
++
++ /** Find all tuples */
++ public Iterator<Tuple<NodeId>> findAll() ;
++
++ /** Return the undelying tuple table - used with great care by tools
++ * that directly manipulate internal structures.
++ */
++ public TupleTable getTupleTable() ;
++
++ /** Return the node table */
++ public NodeTable getNodeTable() ;
++
++ public boolean isEmpty() ;
++
++ /** Clear the tuple table. After this operation, find* will find nothing.
++ * This does not mean all data has been removed - for example, it does not mean
++ * that any node table has been emptied.
++ */
++ public void clear() ;
++
++ // No clear operation - need to manage the tuple table
++ // and node tables separately.
++
++ public long size() ;
++}
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableConcrete.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableConcrete.java
index 0000000,0000000..e2fc5a9
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableConcrete.java
@@@ -1,0 -1,0 +1,240 @@@
++/*
++ * 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.
++ *
++ * See the NOTICE file distributed with this work for additional
++ * information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetupletable ;
++
++import static java.lang.String.format ;
++
++import java.util.Iterator ;
++
++import org.apache.jena.atlas.iterator.Iter ;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.atlas.lib.tuple.TupleFactory ;
++import org.apache.jena.graph.Node ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.lib.TupleLib ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.store.nodetable.NodeTable ;
++import org.seaborne.tdb2.store.tupletable.TupleIndex ;
++import org.seaborne.tdb2.store.tupletable.TupleTable ;
++
++/** Group a tuple table and node table together to provide a real NodeTupleTable */
++public class NodeTupleTableConcrete implements NodeTupleTable
++{
++ protected final NodeTable nodeTable ;
++ protected final TupleTable tupleTable ;
++
++ /*
++ * Concurrency checking: Everything goes through one of
++ * addRow, deleteRow or find*
++ */
++
++ public NodeTupleTableConcrete(int N, TupleIndex[] indexes, NodeTable nodeTable)
++ {
++ if (indexes.length == 0 || indexes[0] == null) throw new TDBException("A primary index is required") ;
++ for (TupleIndex index : indexes)
++ {
++ if (N != index.getTupleLength())
++ throw new TDBException(format("Inconsistent: TupleTable width is %d but index %s is %d",
++ N, index.getMappingStr(), index.getTupleLength())) ;
++ }
++
++ this.tupleTable = new TupleTable(N, indexes) ;
++ this.nodeTable = nodeTable ;
++ }
++
++ private void startWrite() { }
++
++ private void finishWrite() { }
++
++ private void startRead() { }
++
++ private void finishRead() { }
++
++ @Override
++ public void addRow(Node... nodes)
++ {
++ try {
++ startWrite() ;
++ NodeId n[] = new NodeId[nodes.length] ;
++ for (int i = 0; i < nodes.length; i++)
++ n[i] = nodeTable.getAllocateNodeId(nodes[i]) ;
++
++ Tuple<NodeId> t = TupleFactory.create(n) ;
++ tupleTable.add(t) ;
++ } finally
++ {
++ finishWrite() ;
++ }
++ }
++
++ @Override
++ public void deleteRow(Node... nodes)
++ {
++ try
++ {
++ startWrite() ;
++ NodeId n[] = new NodeId[nodes.length] ;
++ for (int i = 0; i < nodes.length; i++)
++ {
++ NodeId id = idForNode(nodes[i]) ;
++ if (NodeId.isDoesNotExist(id)) return ;
++ n[i] = id ;
++ }
++
++ Tuple<NodeId> t = TupleFactory.create(n) ;
++ tupleTable.delete(t) ;
++ } finally
++ {
++ finishWrite() ;
++ }
++ }
++
++ /** Find by node. */
++ @Override
++ public Iterator<Tuple<Node>> find(Node... nodes)
++ {
++ try {
++ startRead() ;
++ Iterator<Tuple<NodeId>> iter1 = findAsNodeIds(nodes) ; // **public call
++ if (iter1 == null)
++ return Iter.nullIterator() ;
++ Iterator<Tuple<Node>> iter2 = TupleLib.convertToNodes(nodeTable, iter1) ;
++ return iteratorControl(iter2) ;
++ } finally { finishRead() ; }
++ }
++
++ /**
++ * Find by node - return an iterator of NodeIds.
++ * Can return "null" (when a node is known to be unknown)
++ * for not found as well as NullIterator (when
++ * no tuples are found (unknown unknown).
++ */
++ @Override
++ public Iterator<Tuple<NodeId>> findAsNodeIds(Node... nodes)
++ {
++ NodeId n[] = new NodeId[nodes.length] ;
++ try {
++ startRead() ;
++ for (int i = 0; i < nodes.length; i++)
++ {
++ NodeId id = idForNode(nodes[i]) ;
++ if (NodeId.isDoesNotExist(id))
++ return Iter.nullIterator() ;
++ n[i] = id ;
++ }
++ return find(n) ; // **public call
++ } finally { finishRead() ; }
++ }
++
++ /** Find by NodeId. */
++ @Override
++ public Iterator<Tuple<NodeId>> find(NodeId... ids)
++ {
++ Tuple<NodeId> tuple = TupleFactory.create(ids) ;
++ return find(tuple) ;
++ }
++
++ /** Find by NodeId. */
++ @Override
++ public Iterator<Tuple<NodeId>> find(Tuple<NodeId> tuple)
++ {
++ // All find/*, except findAll, comes through this operation so startRead/finishRead/checkIterator only needs to happen here.
++ try {
++ startRead() ;
++ // find worker - need also protect iterators that access the node table.
++ Iterator<Tuple<NodeId>> iter = tupleTable.find(tuple) ;
++ return iteratorControl(iter) ;
++ } finally { finishRead() ; }
++ }
++
++ @Override
++ public Iterator<Tuple<NodeId>> findAll()
++ {
++ try {
++ startRead() ;
++ return iteratorControl(tupleTable.getIndex(0).all()) ;
++ } finally { finishRead() ; }
++ }
++
++ // ==== Node
++
++ protected final NodeId idForNode(Node node) {
++ if ( node == null || node == Node.ANY )
++ return NodeId.NodeIdAny ;
++ if ( node.isVariable() )
++ throw new TDBException("Can't pass variables to NodeTupleTable.find*") ;
++ return nodeTable.getNodeIdForNode(node) ;
++ }
++
++ // ==== Accessors
++
++ /**
++ * Return the undelying tuple table - used with great care by tools that directly
++ * manipulate internal structures.
++ */
++ @Override
++ public final TupleTable getTupleTable() {
++ return tupleTable ;
++ }
++
++ /** Return the node table */
++ @Override
++ public final NodeTable getNodeTable() {
++ return nodeTable ;
++ }
++
++ @Override
++ public boolean isEmpty() {
++ return tupleTable.isEmpty() ;
++ }
++
++ /** Clear the tuple table - does not clear the node table */
++ @Override
++ public void clear() {
++ try {
++ startWrite() ;
++ tupleTable.clear() ;
++ } finally { finishWrite() ; }
++ }
++
++ @Override
++ public long size() {
++ return tupleTable.size() ;
++ }
++
++ @Override
++ public final void close() {
++ try {
++ startWrite() ;
++ tupleTable.close() ;
++ nodeTable.close() ;
++ } finally { finishWrite() ; }
++ }
++
++ @Override
++ public final void sync() {
++ try {
++ startWrite() ;
++ tupleTable.sync() ;
++ nodeTable.sync() ;
++ } finally { finishWrite() ; }
++ }
++
++ // Add any iterator checking.
++ private <T> Iterator<T> iteratorControl(Iterator<T> iter) { return iter; }
++}
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableView.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableView.java
index 0000000,0000000..ea24558
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableView.java
@@@ -1,0 -1,0 +1,112 @@@
++/*
++ * 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.
++ *
++ * See the NOTICE file distributed with this work for additional
++ * information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetupletable;
++
++import java.util.Iterator ;
++
++import org.apache.jena.atlas.lib.ArrayUtils ;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.graph.Node ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.store.tupletable.TupleTable ;
++
++/** (Read-only?) projection of another NodeTupleTable.
++ * This will not reduce a N-wide tuple to N-1 when find*() used.
++ */
++public class NodeTupleTableView extends NodeTupleTableWrapper
++{
++ private Node prefix ;
++ private NodeId prefixId ;
++ //private boolean readOnly = false ;
++
++ public NodeTupleTableView(NodeTupleTable ntt, Node prefix)
++ {
++ super(ntt) ;
++ this.prefix = prefix ;
++ this.prefixId = ntt.getNodeTable().getNodeIdForNode(prefix) ;
++ }
++
++ @Override
++ public void addRow(Node... nodes)
++ {
++ nodes = push(Node.class, prefix, nodes) ;
++ super.addRow(nodes) ;
++ }
++
++ @Override
++ public void deleteRow(Node... nodes)
++ {
++ nodes = push(Node.class, prefix, nodes) ;
++ super.deleteRow(nodes) ;
++ }
++
++ @Override
++ public Iterator<Tuple<Node>> find(Node... nodes)
++ {
++ nodes = push(Node.class, prefix, nodes) ;
++ return nodeTupleTable.find(nodes) ;
++ }
++
++ private static <T> T[] push(Class<T> cls, T x, T[] array)
++ {
++ T[] array2 = ArrayUtils.alloc(cls, array.length+1) ;
++ System.arraycopy(array, 0, array2, 1, array.length) ;
++ array2[0] = x ;
++ return array2 ;
++ }
++
++ private static <T> T[] push(Class<T> cls, T x, Tuple<T> tuple)
++ {
++ T[] array2 = ArrayUtils.alloc(cls, tuple.len()+1) ;
++ tuple.copyInto(array2, 1);
++ array2[0] = x ;
++ return array2 ;
++ }
++
++ @Override
++ public Iterator<Tuple<NodeId>> find(NodeId... ids)
++ {
++ ids = push(NodeId.class, prefixId, ids) ;
++ return nodeTupleTable.find(ids) ;
++ }
++
++ @Override
++ public Iterator<Tuple<NodeId>> find(Tuple<NodeId> ids)
++ {
++ NodeId[] ids2 = push(NodeId.class, prefixId, ids) ;
++ return nodeTupleTable.find(ids2) ;
++ }
++
++ @Override
++ public Iterator<Tuple<NodeId>> findAsNodeIds(Node... nodes)
++ {
++ nodes = push(Node.class, prefix, nodes) ;
++ return nodeTupleTable.findAsNodeIds(nodes) ;
++ }
++
++// @Override
++// public boolean isReadOnly() { return readOnly ; }
++//
++// @Override
++// public void setReadOnly(boolean mode) { readOnly = mode ; }
++
++ @Override
++ public TupleTable getTupleTable()
++ // Need a projection of this?
++ { return super.getTupleTable() ; }
++}
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableWrapper.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableWrapper.java
index 0000000,0000000..952dda9
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableWrapper.java
@@@ -1,0 -1,0 +1,97 @@@
++/*
++ * 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.
++ *
++ * See the NOTICE file distributed with this work for additional
++ * information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetupletable;
++
++import java.util.Iterator ;
++
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.graph.Node ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.store.nodetable.NodeTable ;
++import org.seaborne.tdb2.store.tupletable.TupleTable ;
++
++public class NodeTupleTableWrapper implements NodeTupleTable
++{
++ protected NodeTupleTable nodeTupleTable ;
++
++ public NodeTupleTableWrapper(NodeTupleTable ntt) {
++ setNodeTupleTable(ntt) ;
++ }
++
++ protected NodeTupleTable setNodeTupleTable(NodeTupleTable ntt) {
++ NodeTupleTable old = nodeTupleTable ;
++ nodeTupleTable = ntt ;
++ return old ;
++ }
++
++ @Override
++ public void addRow(Node... nodes)
++ { nodeTupleTable.addRow(nodes) ; }
++
++ @Override
++ public void deleteRow(Node... nodes)
++ { nodeTupleTable.deleteRow(nodes) ; }
++
++ @Override
++ public Iterator<Tuple<Node>> find(Node... nodes)
++ { return nodeTupleTable.find(nodes) ; }
++
++ @Override
++ public Iterator<Tuple<NodeId>> find(NodeId... ids)
++ { return nodeTupleTable.find(ids) ; }
++
++ @Override
++ public Iterator<Tuple<NodeId>> find(Tuple<NodeId> tuple)
++ { return nodeTupleTable.find(tuple) ; }
++
++ @Override
++ public Iterator<Tuple<NodeId>> findAsNodeIds(Node... nodes)
++ { return nodeTupleTable.findAsNodeIds(nodes) ; }
++
++ @Override
++ public Iterator<Tuple<NodeId>> findAll()
++ { return nodeTupleTable.findAll() ; }
++
++ @Override
++ public NodeTable getNodeTable()
++ { return nodeTupleTable.getNodeTable() ; }
++
++ @Override
++ public TupleTable getTupleTable()
++ { return nodeTupleTable.getTupleTable() ; }
++
++ @Override
++ public boolean isEmpty()
++ { return nodeTupleTable.isEmpty() ; }
++
++ @Override
++ public void clear()
++ { nodeTupleTable.clear(); }
++
++ @Override
++ public long size()
++ { return nodeTupleTable.size() ; }
++
++ @Override
++ public void sync()
++ { nodeTupleTable.sync() ; }
++
++ @Override
++ public void close()
++ { nodeTupleTable.close() ; }
++}
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndex.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndex.java
index 0000000,0000000..e3c8b47
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndex.java
@@@ -1,0 -1,0 +1,87 @@@
++/*
++ * 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.
++ *
++ * See the NOTICE file distributed with this work for additional
++ * information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.tupletable;
++
++import java.util.Collection ;
++import java.util.Iterator ;
++
++import org.apache.jena.atlas.lib.Closeable ;
++import org.apache.jena.atlas.lib.Sync ;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.atlas.lib.tuple.TupleMap ;
++import org.seaborne.tdb2.store.NodeId ;
++
++public interface TupleIndex extends Sync, Closeable
++{
++ /** Insert a tuple */
++ public void add(Tuple<NodeId> tuple) ;
++
++ /** Delete a tuple */
++ public void delete(Tuple<NodeId> tuple) ;
++
++ /** Insert tuples */
++ public void addAll(Collection<Tuple<NodeId>> tuples) ;
++
++ /** Delete tuples */
++ public void deleteAll(Collection<Tuple<NodeId>> tuples) ;
++
++ /** Get a convenient display string for the index - do not rely on the format */
++ public String getName() ;
++
++ /** Get a convenient display string based on the details of the column map - do not rely on the format */
++ public String getMappingStr() ;
++
++ /** Get the mapping of tuples used by this index */
++ public TupleMap getMapping() ;
++
++ /** Find all matching tuples - a slot of NodeId.NodeIdAny (or null) means match any.
++ * Input pattern in natural order, not index order.
++ */
++ public Iterator<Tuple<NodeId>> find(Tuple<NodeId> pattern) ;
++
++ /** return an iterator of everything */
++ public Iterator<Tuple<NodeId>> all() ;
++
++ /** Weight a pattern - specified in normal order (not index order).
++ * Large numbers means better match. */
++ public int weight(Tuple<NodeId> pattern) ;
++
++ /** Length of tuple supported */
++ public int getTupleLength() ;
++
++ /** Size of index (number of slots). May be an estimate and not exact. -1 for unknown. */
++ public long size() ;
++
++ /** Answer whether empty or not */
++ public boolean isEmpty() ;
++
++ /** Clear the index */
++ public void clear() ;
++
++ /** Return a TupleIndex if this instance wraps another, else return null */
++ public TupleIndex wrapped() ;
++
++ default public TupleIndex baseTupleIndex() {
++ TupleIndex index = this ;
++ TupleIndex index2 = null ;
++ while( (index2 = index.wrapped()) != null ) {
++ index = index2 ;
++ }
++ return index ;
++ }
++}
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndexBase.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndexBase.java
index 0000000,0000000..911950b
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndexBase.java
@@@ -1,0 -1,0 +1,123 @@@
++/*
++ * 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.
++ *
++ * See the NOTICE file distributed with this work for additional
++ * information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.tupletable;
++
++import java.util.Iterator ;
++
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.atlas.lib.tuple.TupleMap ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.store.NodeId ;
++
++public abstract class TupleIndexBase implements TupleIndex
++{
++ private static final boolean Check = false ;
++
++ protected final TupleMap tupleMap ;
++ protected final int tupleLength ;
++
++ private final String name ;
++
++ protected TupleIndexBase(int N, TupleMap indexMapping, String name)
++ {
++ this.tupleLength = N ;
++ this.tupleMap = indexMapping ;
++ this.name = name ;
++ }
++
++ @Override
++ public TupleIndex wrapped() {
++ return null ;
++ }
++
++ /** Add tuple worker: Tuple passed in unmapped (untouched) order */
++ protected abstract void performAdd(Tuple<NodeId> tuple) ;
++
++ /** Delete tuple worker: Tuple passed in unmapped (untouched) order */
++ protected abstract void performDelete(Tuple<NodeId> tuple) ;
++
++ /** Find tuples worker: Tuple passed in unmaped (untouched) order */
++ protected abstract Iterator<Tuple<NodeId>> performFind(Tuple<NodeId> tuple) ;
++
++ /** Insert a tuple */
++ @Override
++ public final void add(Tuple<NodeId> tuple)
++ {
++ if ( Check ) {
++ if ( tupleLength != tuple.len() )
++ throw new TDBException(String.format("Mismatch: tuple length %d / index for length %d", tuple.len(), tupleLength));
++ }
++ performAdd(tuple) ;
++ }
++ /** Delete a tuple */
++ @Override
++ public final void delete(Tuple<NodeId> tuple)
++ {
++ if ( Check ) {
++ if ( tupleLength != tuple.len() )
++ throw new TDBException(String.format("Mismatch: tuple length %d / index for length %d", tuple.len(), tupleLength));
++ }
++
++ performDelete(tuple) ;
++ }
++
++ /** Find all matching tuples - a slot of NodeId.NodeIdAny (or null) means match any.
++ * Input pattern in natural order, not index order.
++ */
++ @Override
++ public final Iterator<Tuple<NodeId>> find(Tuple<NodeId> pattern)
++ {
++ if ( Check ) {
++ if ( tupleLength != pattern.len() )
++ throw new TDBException(String.format("Mismatch: tuple length %d / index for length %d", pattern.len(), tupleLength));
++ }
++ // null to NodeId.NodIdAny ??
++ return performFind(pattern) ;
++ }
++
++ @Override
++ public final int weight(Tuple<NodeId> pattern)
++ {
++ for ( int i = 0 ; i < tupleLength ; i++ )
++ {
++ NodeId X = tupleMap.mapSlot(i, pattern) ;
++ if ( undef(X) )
++ // End of fixed terms
++ return i ;
++ }
++ return tupleLength ;
++ }
++
++ @Override
++ public final String getMappingStr() { return tupleMap.getLabel() ; }
++
++ @Override
++ public final String getName() { return name ; }
++
++ @Override
++ public final int getTupleLength() { return tupleLength ; }
++
++ @Override
++ public final TupleMap getMapping() { return tupleMap ; }
++
++ protected final boolean undef(NodeId x)
++ { return NodeId.isAny(x) ; }
++
++ @Override
++ public String toString() { return "index:"+getName() ; }
++}
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndexRecord.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndexRecord.java
index 0000000,0000000..c723472
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndexRecord.java
@@@ -1,0 -1,0 +1,237 @@@
++/*
++ * 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.
++ *
++ * See the NOTICE file distributed with this work for additional
++ * information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.tupletable;
++
++import static java.lang.String.format ;
++import static org.seaborne.tdb2.sys.SystemTDB.SizeOfNodeId ;
++
++import java.util.Collection ;
++import java.util.Iterator ;
++import java.util.function.Predicate ;
++
++import org.apache.jena.atlas.iterator.Iter ;
++import org.apache.jena.atlas.iterator.NullIterator ;
++import org.apache.jena.atlas.iterator.SingletonIterator ;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.atlas.lib.tuple.TupleMap ;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.index.RangeIndex ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.lib.TupleLib ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.store.NodeIdFactory;
++
++public class TupleIndexRecord extends TupleIndexBase
++{
++ private static final boolean Check = false ;
++ private RangeIndex index ;
++ private RecordFactory factory ;
++
++ public TupleIndexRecord(int N, TupleMap tupleMapping, String name, RecordFactory factory, RangeIndex index)
++ {
++ super(N, tupleMapping, name) ;
++ this.factory = factory ;
++ this.index = index ;
++
++ if ( factory.keyLength() != N*SizeOfNodeId)
++ throw new TDBException(format("Mismatch: TupleIndex of length %d is not comparative with a factory for key length %d", N, factory.keyLength())) ;
++ }
++
++ /** Insert a tuple */
++ @Override
++ protected void performAdd(Tuple<NodeId> tuple) {
++ Record r = TupleLib.record(factory, tuple, tupleMap) ;
++ index.insert(r) ;
++ }
++
++ /** Delete a tuple */
++ @Override
++ protected void performDelete(Tuple<NodeId> tuple) {
++ Record r = TupleLib.record(factory, tuple, tupleMap) ;
++ index.delete(r) ;
++ }
++
++ /** Insert tuples */
++ @Override
++ public void addAll(Collection<Tuple<NodeId>> tuples) {
++ for ( Tuple<NodeId> t : tuples )
++ add(t) ;
++ }
++
++ /** Delete tuples */
++ @Override
++ public void deleteAll(Collection<Tuple<NodeId>> tuples) {
++ for ( Tuple<NodeId> t : tuples )
++ delete(t) ;
++ }
++
++ /** Find all matching tuples - a slot of NodeId.NodeIdAny (or null) means match any.
++ * Input pattern in natural order, not index order.
++ */
++
++ @Override
++ protected Iterator<Tuple<NodeId>> performFind(Tuple<NodeId> pattern) {
++ return findOrScan(pattern) ;
++ }
++
++ // Package visibility for testing.
++ final Iterator<Tuple<NodeId>> findOrScan(Tuple<NodeId> pattern) {
++ return findWorker(pattern, true, true) ;
++ }
++
++ final Iterator<Tuple<NodeId>> findOrPartialScan(Tuple<NodeId> pattern) {
++ return findWorker(pattern, true, false) ;
++ }
++
++ final Iterator<Tuple<NodeId>> findByIndex(Tuple<NodeId> pattern) {
++ return findWorker(pattern, false, false) ;
++ }
++
++ private Iterator<Tuple<NodeId>> findWorker(Tuple<NodeId> patternNaturalOrder, boolean partialScanAllowed, boolean fullScanAllowed) {
++ if ( Check )
++ {
++ if ( tupleLength != patternNaturalOrder.len() )
++ throw new TDBException(String.format("Mismatch: tuple length %d / index for length %d", patternNaturalOrder.len(), tupleLength)) ;
++ }
++
++ // Convert to index order.
++ Tuple<NodeId> pattern = tupleMap.map(patternNaturalOrder) ;
++
++ // Canonical form.
++ int numSlots = 0 ;
++ int leadingIdx = -2; // Index of last leading pattern NodeId. Start less than numSlots-1
++ boolean leading = true ;
++
++ // Records.
++ Record minRec = factory.createKeyOnly() ;
++ Record maxRec = factory.createKeyOnly() ;
++
++ // Set the prefixes.
++ for ( int i = 0 ; i < pattern.len() ; i++ ) {
++ NodeId X = pattern.get(i) ;
++ if ( NodeId.isAny(X) ) {
++ X = null ;
++ // No longer seting leading key slots.
++ leading = false ;
++ continue ;
++ }
++// if ( NodeId.isDoesNotExist(X) )
++// return Iter.nullIterator();
++
++ numSlots++ ;
++ if ( leading ) {
++ leadingIdx = i ;
++ NodeIdFactory.set(X, minRec.getKey(), i*SizeOfNodeId) ;
++ NodeIdFactory.set(X, maxRec.getKey(), i*SizeOfNodeId) ;
++ }
++ }
++
++ // Is it a simple existence test?
++ if ( numSlots == pattern.len() ) {
++ if ( index.contains(minRec) )
++ return new SingletonIterator<>(pattern) ;
++ else
++ return new NullIterator<>() ;
++ }
++
++ Iterator<Record> iter = null ;
++
++ if ( leadingIdx < 0 ) {
++ if ( ! fullScanAllowed )
++ return null ;
++ //System.out.println("Full scan") ;
++ // Full scan necessary
++ iter = index.iterator() ;
++ } else {
++ // Adjust the maxRec.
++ NodeId X = pattern.get(leadingIdx) ;
++ // Set the max Record to the leading NodeIds, +1.
++ // Example, SP? inclusive to S(P+1)? exclusive where ? is zero.
++ NodeIdFactory.setNext(X, maxRec.getKey(), leadingIdx*SizeOfNodeId) ;
++ iter = index.iterator(minRec, maxRec) ;
++ }
++
++ Iterator<Tuple<NodeId>> tuples = Iter.map(iter, item -> TupleLib.tuple(item, tupleMap)) ;
++
++ if ( leadingIdx < numSlots-1 ) {
++ if ( ! partialScanAllowed )
++ return null ;
++ // Didn't match all defined slots in request.
++ // Partial or full scan needed.
++ //pattern.unmap(colMap) ;
++ tuples = scan(tuples, patternNaturalOrder) ;
++ }
++
++ return tuples ;
++ }
++
++ @Override
++ public Iterator<Tuple<NodeId>> all()
++ {
++ Iterator<Record> iter = index.iterator() ;
++ return Iter.map(iter, item -> TupleLib.tuple(item, tupleMap)) ;
++ }
++
++ private Iterator<Tuple<NodeId>> scan(Iterator<Tuple<NodeId>> iter, Tuple<NodeId> pattern) {
++ Predicate<Tuple<NodeId>> filter = (item) -> {
++ // Check on pattern and item (both in natural order)
++ for ( int i = 0 ; i < tupleLength ; i++ ) {
++ NodeId n = pattern.get(i) ;
++ // The pattern must be null/Any or match the tuple being tested.
++ if ( ! NodeId.isAny(n) )
++ if ( ! item.get(i).equals(n) )
++ return false ;
++ }
++ return true ;
++ } ;
++
++ return Iter.filter(iter, filter) ;
++ }
++
++ @Override
++ public void close() {
++ index.close() ;
++ }
++
++ @Override
++ public void sync() {
++ index.sync() ;
++ }
++
++ public final RangeIndex getRangeIndex() {
++ return index ;
++ }
++
++ //protected final RecordFactory getRecordFactory() { return factory ; }
++
++ @Override
++ public boolean isEmpty() {
++ return index.isEmpty() ;
++ }
++
++ @Override
++ public void clear() {
++ index.clear() ;
++ }
++
++ @Override
++ public long size() {
++ return index.size() ;
++ }
++}
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndexRecordAsyncBulkAdd.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndexRecordAsyncBulkAdd.java
index 0000000,0000000..242232e
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndexRecordAsyncBulkAdd.java
@@@ -1,0 -1,0 +1,262 @@@
++/*
++ * 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.
++ *
++ * See the NOTICE file distributed with this work for additional
++ * information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.tupletable;
++
++import static java.lang.String.format ;
++import static org.seaborne.tdb2.sys.SystemTDB.SizeOfNodeId ;
++
++import java.util.Collection ;
++import java.util.Iterator ;
++import java.util.function.Predicate ;
++
++import org.apache.jena.atlas.iterator.Iter ;
++import org.apache.jena.atlas.iterator.NullIterator ;
++import org.apache.jena.atlas.iterator.SingletonIterator ;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.atlas.lib.tuple.TupleMap ;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.index.RangeIndex ;
++import org.seaborne.dboe.transaction.txn.Transaction ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.lib.Async ;
++import org.seaborne.tdb2.lib.TupleLib ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.store.NodeIdFactory;
++
++// Async addAll
++public class TupleIndexRecordAsyncBulkAdd extends TupleIndexBase
++{
++ private static final boolean Check = false ;
++ private RangeIndex index ;
++ private RecordFactory factory ;
++
++ public TupleIndexRecordAsyncBulkAdd(int N, TupleMap tupleMapping, String name, RecordFactory factory, RangeIndex index)
++ {
++ super(N, tupleMapping, name) ;
++ this.factory = factory ;
++ this.index = index ;
++
++ if ( factory.keyLength() != N*SizeOfNodeId)
++ throw new TDBException(format("Mismatch: TupleIndex of length %d is not comparative with a factory for key length %d", N, factory.keyLength())) ;
++ }
++
++ /** Insert a tuple */
++ @Override
++ protected void performAdd(Tuple<NodeId> tuple) {
++ switchToSync() ;
++ Record r = TupleLib.record(factory, tuple, tupleMap) ;
++ index.insert(r) ;
++ }
++
++ /** Delete a tuple */
++ @Override
++ protected void performDelete(Tuple<NodeId> tuple) {
++ switchToSync() ;
++ Record r = TupleLib.record(factory, tuple, tupleMap) ;
++ index.delete(r) ;
++ }
++
++ private void switchToSync() {
++ async.completeAsyncOperations();
++ }
++
++ Async async = new Async(1,2) ;
++ Object lock = new Object() ;
++
++ /** Insert tuples */
++ @Override
++ public void addAll(Collection<Tuple<NodeId>> tuples) {
++ Transaction txn = null ;
++ // pass into async block.
++
++ async.execAsync(lock, () -> {
++ // Transaction?
++
++ System.out.println(">>Async") ;
++ for ( Tuple<NodeId> t : tuples ) {
++ Record r = TupleLib.record(factory, t, tupleMap) ;
++ index.insert(r) ;
++ }
++ System.out.println("<<Async") ;
++ }) ;
++ }
++
++ /** Delete tuples */
++ @Override
++ public void deleteAll(Collection<Tuple<NodeId>> tuples) {
++ tuples.stream().forEach(this::delete);
++ }
++
++ /** Find all matching tuples - a slot of NodeId.NodeIdAny (or null) means match any.
++ * Input pattern in natural order, not index order.
++ */
++
++ @Override
++ protected Iterator<Tuple<NodeId>> performFind(Tuple<NodeId> pattern) {
++ switchToSync() ;
++ return findOrScan(pattern) ;
++ }
++
++ // Package visibility for testing.
++ final Iterator<Tuple<NodeId>> findOrScan(Tuple<NodeId> pattern) {
++ return findWorker(pattern, true, true) ;
++ }
++
++ final Iterator<Tuple<NodeId>> findOrPartialScan(Tuple<NodeId> pattern) {
++ return findWorker(pattern, true, false) ;
++ }
++
++ final Iterator<Tuple<NodeId>> findByIndex(Tuple<NodeId> pattern) {
++ return findWorker(pattern, false, false) ;
++ }
++
++ private Iterator<Tuple<NodeId>> findWorker(Tuple<NodeId> patternNaturalOrder, boolean partialScanAllowed, boolean fullScanAllowed) {
++ switchToSync();
++ if ( Check && tupleLength != patternNaturalOrder.len() )
++ throw new TDBException(String.format("Mismatch: tuple length %d / index for length %d", patternNaturalOrder.len(), tupleLength));
++
++ // Convert to index order.
++ Tuple<NodeId> pattern = tupleMap.map(patternNaturalOrder) ;
++
++ // Canonical form.
++ int numSlots = 0 ;
++ int leadingIdx = -2; // Index of last leading pattern NodeId. Start less than numSlots-1
++ boolean leading = true ;
++
++ // Records.
++ Record minRec = factory.createKeyOnly() ;
++ Record maxRec = factory.createKeyOnly() ;
++
++ // Set the prefixes.
++ for ( int i = 0 ; i < pattern.len() ; i++ ) {
++ NodeId X = pattern.get(i) ;
++ if ( NodeId.isAny(X) ) {
++ X = null ;
++ // No longer setting leading key slots.
++ leading = false ;
++ continue ;
++ }
++
++ numSlots++ ;
++ if ( leading ) {
++ leadingIdx = i ;
++ NodeIdFactory.set(X, minRec.getKey(), i*SizeOfNodeId) ;
++ NodeIdFactory.set(X, maxRec.getKey(), i*SizeOfNodeId) ;
++ }
++ }
++
++ // Is it a simple existence test?
++ if ( numSlots == pattern.len() ) {
++ if ( index.contains(minRec) )
++ return new SingletonIterator<>(pattern) ;
++ else
++ return new NullIterator<>() ;
++ }
++
++ Iterator<Record> iter = null ;
++
++ if ( leadingIdx < 0 ) {
++ if ( ! fullScanAllowed )
++ return null ;
++ //System.out.println("Full scan") ;
++ // Full scan necessary
++ iter = index.iterator() ;
++ } else {
++ // Adjust the maxRec.
++ NodeId X = pattern.get(leadingIdx) ;
++ // Set the max Record to the leading NodeIds, +1.
++ // Example, SP? inclusive to S(P+1)? exclusive where ? is zero.
++ NodeIdFactory.setNext(X, maxRec.getKey(), leadingIdx*SizeOfNodeId) ;
++ iter = index.iterator(minRec, maxRec) ;
++ }
++
++ Iterator<Tuple<NodeId>> tuples = Iter.map(iter, item -> TupleLib.tuple(item, tupleMap)) ;
++
++ if ( leadingIdx < numSlots-1 ) {
++ if ( ! partialScanAllowed )
++ return null ;
++ // Didn't match all defined slots in request.
++ // Partial or full scan needed.
++ //pattern.unmap(colMap) ;
++ tuples = scan(tuples, patternNaturalOrder) ;
++ }
++
++ return tuples ;
++ }
++
++ @Override
++ public Iterator<Tuple<NodeId>> all()
++ {
++ switchToSync() ;
++ Iterator<Record> iter = index.iterator() ;
++ return Iter.map(iter, item -> TupleLib.tuple(item, tupleMap)) ;
++ }
++
++ private Iterator<Tuple<NodeId>> scan(Iterator<Tuple<NodeId>> iter, Tuple<NodeId> pattern) {
++ Predicate<Tuple<NodeId>> filter = (item) -> {
++ // Check on pattern and item (both in natural order)
++ for ( int i = 0 ; i < tupleLength ; i++ ) {
++ NodeId n = pattern.get(i) ;
++ // The pattern must be null/Any or match the tuple being tested.
++ if ( ! NodeId.isAny(n) )
++ if ( ! item.get(i).equals(n) )
++ return false ;
++ }
++ return true ;
++ } ;
++
++ return Iter.filter(iter, filter) ;
++ }
++
++ @Override
++ public void close() {
++ switchToSync() ;
++ index.close() ;
++ }
++
++ @Override
++ public void sync() {
++ switchToSync() ;
++ index.sync() ;
++ }
++
++ public final RangeIndex getRangeIndex() {
++ return index ;
++ }
++
++ //protected final RecordFactory getRecordFactory() { return factory ; }
++
++ @Override
++ public boolean isEmpty() {
++ switchToSync() ;
++ return index.isEmpty() ;
++ }
++
++ @Override
++ public void clear() {
++ switchToSync() ;
++ index.clear() ;
++ }
++
++ @Override
++ public long size() {
++ switchToSync() ;
++ return index.size() ;
++ }
++}
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndexWrapper.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndexWrapper.java
index 0000000,0000000..43f4b72
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndexWrapper.java
@@@ -1,0 -1,0 +1,117 @@@
++/*
++ * 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.
++ *
++ * See the NOTICE file distributed with this work for additional
++ * information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.tupletable;
++
++import java.util.Collection ;
++import java.util.Iterator ;
++
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.atlas.lib.tuple.TupleMap ;
++import org.seaborne.tdb2.store.NodeId ;
++
++public class TupleIndexWrapper implements TupleIndex
++{
++ protected final TupleIndex index ;
++
++ public TupleIndexWrapper(TupleIndex index) { this.index = index ; }
++
++ @Override
++ public final TupleIndex wrapped() {
++ return index ;
++ }
++
++ @Override
++ public void add(Tuple<NodeId> tuple) {
++ index.add(tuple) ;
++ }
++
++ @Override
++ public void addAll(Collection<Tuple<NodeId>> tuples) {
++ index.addAll(tuples) ;
++ }
++
++ @Override
++ public void delete(Tuple<NodeId> tuple) {
++ index.delete(tuple) ;
++ }
++
++ @Override
++ public void deleteAll(Collection<Tuple<NodeId>> tuples) {
++ index.deleteAll(tuples);
++ }
++
++ @Override
++ public Iterator<Tuple<NodeId>> find(Tuple<NodeId> pattern) {
++ return index.find(pattern) ;
++ }
++
++ @Override
++ public Iterator<Tuple<NodeId>> all() {
++ return index.all() ;
++ }
++
++ @Override
++ public int getTupleLength() {
++ return index.getTupleLength() ;
++ }
++
++ @Override
++ public String getMappingStr() {
++ return index.getMappingStr() ;
++ }
++
++ @Override
++ public TupleMap getMapping() {
++ return index.getMapping() ;
++ }
++
++ @Override
++ public String getName() {
++ return index.getName() ;
++ }
++
++ @Override
++ public int weight(Tuple<NodeId> pattern) {
++ return index.weight(pattern) ;
++ }
++
++ @Override
++ public long size() {
++ return index.size() ;
++ }
++
++ @Override
++ public boolean isEmpty() {
++ return index.isEmpty() ;
++ }
++
++ @Override
++ public void clear() {
++ index.clear() ;
++ }
++
++ @Override
++ public void sync() {
++ index.sync() ;
++ }
++
++ @Override
++ public void close() {
++ index.close() ;
++ }
++}
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleTable.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleTable.java
index 0000000,0000000..d02df70
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleTable.java
@@@ -1,0 -1,0 +1,218 @@@
++/*
++ * 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.
++ *
++ * See the NOTICE file distributed with this work for additional
++ * information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.tupletable;
++
++import static java.lang.String.format ;
++
++import java.util.Iterator ;
++import java.util.List ;
++
++import org.apache.jena.atlas.iterator.Iter;
++import org.apache.jena.atlas.lib.Closeable ;
++import org.apache.jena.atlas.lib.Sync ;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.atlas.logging.Log ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.sys.SystemTDB ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++/** A TupleTable is a set of TupleIndexes. The first TupleIndex is the "primary" index and must exist */
++public class TupleTable implements Sync, Closeable
++{
++ private static Logger log = LoggerFactory.getLogger(TupleTable.class) ;
++
++ private final TupleIndex[] indexes ;
++ private final TupleIndex scanAllIndex ; // Use this index if a complete scan is needed.
++ private final int tupleLen ;
++ private boolean syncNeeded = false ;
++
++ public TupleTable(int tupleLen, TupleIndex[] indexes)
++ {
++ this.tupleLen = tupleLen ;
++ this.indexes = indexes ;
++ if ( indexes[0] == null )
++ throw new TDBException("TupleTable: no primary index") ;
++ for ( TupleIndex index : indexes )
++ {
++ if ( index != null && index.getTupleLength() != tupleLen )
++ throw new TDBException("Incompatible index: "+index.getMappingStr()) ;
++ }
++ scanAllIndex = chooseScanAllIndex(tupleLen, indexes) ;
++ }
++
++ /** Choose an index to scan in case we are asked for everything
++ * This needs to be ???G for the distinctAdjacent filter in union query to work.
++ */
++ private static TupleIndex chooseScanAllIndex(int tupleLen, TupleIndex[] indexes)
++ {
++ if ( tupleLen != 4 )
++ return indexes[0] ;
++
++ for ( TupleIndex index : indexes )
++ {
++ // First look for SPOG
++ if ( index.getName().equals("SPOG") )
++ return index ;
++ }
++
++ for ( TupleIndex index : indexes )
++ {
++ // Then look for any ???G
++ if ( index.getName().endsWith("G") )
++ return index ;
++ }
++
++ Log.warn(SystemTDB.errlog, "Did not find a ???G index for full scans") ;
++ return indexes[0] ;
++ }
++
++ /** Insert a tuple */
++ public void add(Tuple<NodeId> t) {
++ // A "contains test" could be used to avoid needing to hit all
++ // the indexes when the triple is already present.
++ if ( tupleLen != t.len() )
++ throw new TDBException(format("Mismatch: inserting tuple of length %d into a table of tuples of length %d", t.len(), tupleLen)) ;
++ for ( int i = 0 ; i < indexes.length ; i++ ) {
++ if ( indexes[i] == null ) continue ;
++ indexes[i].add(t) ;
++ syncNeeded = true ;
++ }
++ }
++
++ /** Insert tuples */
++ public void addAll(List<Tuple<NodeId>> t) {
++ // Parallel.
++ for ( int i = 0 ; i < indexes.length ; i++ ) {
++ if ( indexes[i] == null ) continue ;
++ indexes[i].addAll(t) ;
++ syncNeeded = true ;
++ }
++ }
++
++ /** Delete a tuple */
++ public void delete( Tuple<NodeId> t ) {
++ if ( tupleLen != t.len() )
++ throw new TDBException(format("Mismatch: deleting tuple of length %d from a table of tuples of length %d", t.len(), tupleLen)) ;
++
++ for ( TupleIndex index : indexes ) {
++ if ( index == null )
++ continue;
++ index.delete( t );
++ }
++ }
++
++ /** Delete tuples */
++ public void deleteAll(List<Tuple<NodeId>> t) {
++ // Parallel.
++ for ( int i = 0 ; i < indexes.length ; i++ ) {
++ if ( indexes[i] == null ) continue ;
++ indexes[i].deleteAll(t) ;
++ syncNeeded = true ;
++ }
++ }
++
++ /** Find all matching tuples - a slot of NodeId.NodeIdAny means match any */
++ public Iterator<Tuple<NodeId>> find(Tuple<NodeId> pattern) {
++ if ( tupleLen != pattern.len() )
++ throw new TDBException(format("Mismatch: finding tuple of length %d in a table of tuples of length %d", pattern.len(), tupleLen)) ;
++
++ int numSlots = 0 ;
++ // Canonical form.
++ for ( int i = 0 ; i < tupleLen ; i++ ) {
++ NodeId x = pattern.get(i) ;
++ if ( ! NodeId.isAny(x) )
++ numSlots++ ;
++ if ( NodeId.isDoesNotExist(x))
++ return Iter.nullIterator();
++ }
++
++ if ( numSlots == 0 )
++ return scanAllIndex.all() ;
++
++ int indexNumSlots = 0 ;
++ TupleIndex index = null ;
++ for ( TupleIndex idx : indexes ) {
++ if ( idx != null ) {
++ int w = idx.weight( pattern );
++ if ( w > indexNumSlots ) {
++ indexNumSlots = w;
++ index = idx;
++ }
++ }
++ }
++
++ if ( index == null )
++ // No index at all. Scan.
++ index = indexes[0] ;
++ return index.find(pattern) ;
++ }
++
++ @Override
++ final public void close() {
++ for ( TupleIndex idx : indexes ) {
++ if ( idx != null )
++ idx.close();
++ }
++ }
++
++ @Override
++ public void sync() {
++ if ( syncNeeded ) {
++ for ( TupleIndex idx : indexes ) {
++ if ( idx != null )
++ idx.sync() ;
++ }
++ syncNeeded = false ;
++ }
++ }
++
++ public boolean isEmpty() { return indexes[0].isEmpty() ; }
++
++ public void clear() {
++ for ( TupleIndex idx : indexes ) {
++ if ( idx != null )
++ idx.clear() ;
++ }
++ syncNeeded = true ;
++ }
++
++ public long size() {
++ return indexes[0].size() ;
++ }
++
++ /** Get i'th index */
++ public TupleIndex getIndex(int i) { return indexes[i] ; }
++
++ /** Get all indexes - for code that maipulates internal structures directly - use with care */
++ public TupleIndex[] getIndexes() { return indexes ; }
++
++ /** Get the width of tuples in indexes in this table */
++ public int getTupleLen() { return tupleLen ; }
++
++ /** Set index - for code that manipulates internal structures directly - use with care */
++ public void setTupleIndex(int i, TupleIndex index) {
++ if ( index != null && index.getTupleLength() != tupleLen )
++ throw new TDBException("Incompatible index: " + index.getMappingStr()) ;
++ indexes[i] = index ;
++ }
++
++ /** Number of indexes on this tuple table */
++ public int numIndexes() { return indexes.length ; }
++}
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/DateTimeNode.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/DateTimeNode.java
index 0000000,0000000..0853938
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/DateTimeNode.java
@@@ -1,0 -1,0 +1,253 @@@
++/*
++ * 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.
++ *
++ * See the NOTICE file distributed with this work for additional
++ * information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.value;
++
++import java.math.BigDecimal ;
++
++import javax.xml.datatype.DatatypeConfigurationException ;
++import javax.xml.datatype.DatatypeConstants ;
++import javax.xml.datatype.DatatypeFactory ;
++import javax.xml.datatype.XMLGregorianCalendar ;
++
++import org.apache.jena.atlas.lib.BitsInt ;
++import org.apache.jena.atlas.lib.BitsLong ;
++import org.apache.jena.atlas.lib.NumberUtils ;
++import org.seaborne.tdb2.TDBException ;
++
++public class DateTimeNode
++{
++ // ---- Layout
++ // Epoch base: 0000-01-01T00:00:00
++
++ // Layout:
++ // Bits 56-63 : type
++
++ // Bits 49-55 (7 bits) : timezone -- 15 min precision + special for Z and no timezone.
++ // Bits 27-48 (22 bits) : date, year is 13 bits = 8000 years (0 to 7999)
++ // Bits 0-26 (27 bits) : time, to milliseconds
++
++ // Layout:
++ // Hi: TZ YYYY MM DD HH MM SS.sss Lo:
++ // Looses the distinction between 00:00:00 vs 24:00:00 (of the day before).
++
++ // Const-ize
++ // 13 bits year, 4 bits month, 5 bits day => 22 bits
++ static final int DATE_LEN = 22;
++ // 5 bits hour + 6 bits minute + 16 bits seconds (to millisecond)
++ static final int TIME_LEN = 27;
++
++ static final int MILLI = 0;
++ static final int MILLI_LEN = 16;
++
++ static final int MINUTES = MILLI_LEN;
++ static final int MINUTES_LEN = 6;
++
++ static final int HOUR = MILLI_LEN + MINUTES_LEN;
++ static final int HOUR_LEN = 5;
++
++ static final int DAY = TIME_LEN;
++ static final int DAY_LEN = 5;
++
++ static final int MONTH = TIME_LEN + DAY_LEN;
++ static final int MONTH_LEN = 4;
++
++ static final int YEAR = TIME_LEN + MONTH_LEN + DAY_LEN;
++ static final int YEAR_LEN = 13;
++
++ static final int TZ = TIME_LEN + DATE_LEN;
++ static final int TZ_LEN = 7;
++ // Value for Z
++ static final int TZ_Z = 0x7F;
++ // Value for no timezone.
++ static final int TZ_NONE = 0x7E;
++
++ static DatatypeFactory datatypeFactory = null;
++ static {
++ try {
++ datatypeFactory = DatatypeFactory.newInstance();
++ }
++ catch (DatatypeConfigurationException ex) {
++ throw new TDBException("DateTimeNode", ex);
++ }
++ }
++
++ // Packed in correct place.
++ static long time(long v, int hour, int mins, int millisec) {
++ // And bit offset for direct packing?
++ // HH:MM:SS.ssss => 5 bits H, 6 bits M, 16 bits S ==> 27 bits
++ v = BitsLong.pack(v, hour, HOUR, HOUR + HOUR_LEN);
++ v = BitsLong.pack(v, mins, MINUTES, MINUTES + MINUTES_LEN);
++ v = BitsLong.pack(v, millisec, MILLI, MILLI + MILLI_LEN);
++ return v;
++ }
++
++ // Packed in correct place.
++ static long date(long v, int year, int month, int day) {
++ // YYYY:MM:DD => 13 bits year, 4 bits month, 5 bits day => 22 bits
++ v = BitsLong.pack(v, year, YEAR, YEAR + YEAR_LEN);
++ v = BitsLong.pack(v, month, MONTH, MONTH + MONTH_LEN);
++ v = BitsLong.pack(v, day, DAY, DAY + DAY_LEN);
++ return v;
++ }
++
++ static long tz(long v, int tz_in_quarters) {
++ v = BitsLong.pack(v, tz_in_quarters, TZ, TZ + TZ_LEN);
++ return v;
++ }
++
++ // From string. Assumed legal. Retains all info this way.
++ // returns -1 for unpackable.
++ public static long packDate(String lex) {
++ return packDateTime(lex);
++ }
++
++ // From string. Assumed legal.
++ // Returns -1 for unpackable.
++
++ public static long packDateTime(String lex) {
++ try {
++ return packDateTime$(lex);
++ }
++ catch (Exception ex) {
++ return -1;
++ }
++ }
++
++ private static long packDateTime$(String lex) {
++ long v = 0;
++ // Whitespace facet processing.
++ lex = lex.trim();
++
++ boolean containsZ = (lex.indexOf('Z') > 0);
++
++ // Bug in Java 1.6 (build 5 at least)
++ // T24:00:00 not accepted.
++ // See also TestNodeId.nodeId_date_time_7
++
++ XMLGregorianCalendar xcal = datatypeFactory.newXMLGregorianCalendar(lex);
++
++ if ( xcal.getFractionalSecond() != null ) {
++ BigDecimal fs = xcal.getFractionalSecond();
++ // Were there sub-millisecond resolution fractional seconds?
++ // This isn't perfect but it needs a very long fractional part to break it,
++ // less than observable quantum of time.
++ if ( fs.doubleValue() != xcal.getMillisecond() / 1000.0 )
++ return -1;
++ }
++
++ int y = xcal.getYear();
++
++ if ( y < 0 || y >= 8000 )
++ return -1;
++
++ v = date(v, xcal.getYear(), xcal.getMonth(), xcal.getDay());
++ v = time(v, xcal.getHour(), xcal.getMinute(), xcal.getSecond() * 1000 + xcal.getMillisecond());
++
++ if ( containsZ )
++ return tz(v, TZ_Z);
++
++ int tz = xcal.getTimezone();
++ if ( tz == DatatypeConstants.FIELD_UNDEFINED )
++ return tz(v, TZ_NONE);
++
++ // Timezone is weird.
++ if ( tz % 15 != 0 )
++ return -1;
++
++ tz = tz / 15;
++ return tz(v, tz);
++ }
++
++ public static String unpackDateTime(long v) {
++ return unpack(v, true);
++ }
++
++ public static String unpackDate(long v) {
++ return unpack(v, false);
++ }
++
++ // Avoid calls to String.format
++ private static String unpack(long v, boolean isDateTime) {
++ // YYYY:MM:DD => 13 bits year, 4 bits month, 5 bits day => 22 bits
++ int years = (int)BitsLong.unpack(v, YEAR, YEAR + YEAR_LEN);
++ int months = (int)BitsLong.unpack(v, MONTH, MONTH + MONTH_LEN);
++ int days = (int)BitsLong.unpack(v, DAY, DAY + DAY_LEN);
++
++ // Hours: 5, mins 6, milli 16, TZ 7 => 34 bits
++ int hours = (int)BitsLong.unpack(v, HOUR, HOUR + HOUR_LEN);
++ int minutes = (int)BitsLong.unpack(v, MINUTES, MINUTES + MINUTES_LEN);
++ int milliSeconds = (int)BitsLong.unpack(v, MILLI, MILLI + MILLI_LEN);
++
++ int tz = (int)BitsLong.unpack(v, TZ, TZ + TZ_LEN);
++
++ int sec = milliSeconds / 1000;
++ int fractionSec = milliSeconds % 1000;
++
++ StringBuilder sb = new StringBuilder(50);
++ NumberUtils.formatInt(sb, years, 4);
++ sb.append('-');
++ NumberUtils.formatInt(sb, months, 2);
++ sb.append('-');
++ NumberUtils.formatInt(sb, days, 2);
++ if ( isDateTime ) {
++ sb.append('T');
++ NumberUtils.formatInt(sb, hours, 2);
++ sb.append(':');
++ NumberUtils.formatInt(sb, minutes, 2);
++ sb.append(':');
++ NumberUtils.formatInt(sb, sec, 2);
++
++ // Formatting needed : int->any
++ if ( fractionSec != 0 ) {
++ sb.append(".");
++ if ( fractionSec % 100 == 0 )
++ NumberUtils.formatInt(sb, fractionSec / 100, 1);
++ else if ( fractionSec % 10 == 0 )
++ NumberUtils.formatInt(sb, fractionSec / 10, 2);
++ else
++ NumberUtils.formatInt(sb, fractionSec, 3);
++ }
++ }
++ // tz in 15min units
++ // Special values.
++ if ( tz == TZ_Z ) {
++ sb.append("Z");
++ return sb.toString();
++ }
++
++ if ( tz == TZ_NONE )
++ return sb.toString();
++
++ // Sign extend.
++ if ( BitsLong.isSet(v, TZ + TZ_LEN - 1) )
++ tz = BitsInt.set(tz, TZ_LEN, 32);
++
++ if ( tz < 0 ) {
++ tz = -tz;
++ sb.append('-');
++ } else
++ sb.append('+');
++
++ int tzH = tz / 4;
++ int tzM = (tz % 4) * 15;
++ NumberUtils.formatUnsignedInt(sb, tzH, 2);
++ sb.append(':');
++ NumberUtils.formatUnsignedInt(sb, tzM, 2);
++ return sb.toString();
++ }
++}
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/DecimalNode.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/DecimalNode.java
index 0000000,0000000..2e03d96
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/DecimalNode.java
@@@ -1,0 -1,0 +1,22 @@@
++/*
++ * 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.
++ *
++ * See the NOTICE file distributed with this work for additional
++ * information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.value;
++
++/** Placeholder for a full length decimal , using int+long.*/
++public class DecimalNode {
++}
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/DecimalNode56.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/DecimalNode56.java
index 0000000,0000000..c36e5c5
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/DecimalNode56.java
@@@ -1,0 -1,0 +1,140 @@@
++/*
++ * 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.
++ *
++ * See the NOTICE file distributed with this work for additional
++ * information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.value;
++
++import java.math.BigDecimal;
++import java.math.BigInteger;
++
++import org.apache.jena.atlas.lib.BitsLong ;
++
++
++// Decimal packed into 56 bits.
++public class DecimalNode56
++{
++ //private static Logger log = LoggerFactory.getLogger(DecimalNode.class) ;
++
++ BigDecimal decimal = null ;
++
++ // signed 8 bits of scale, signed 48 bits of value.
++ // Decimal precision is 47 bits (it's signed) or around 14 places.
++ // Not finance industry accuracy nor XSD (18 places minimum) but still useful.
++
++ static final int SCALE_LEN = 8;
++ static final int VALUE_LEN = 48;
++ static final int ENC_LEN = 48 + SCALE_LEN;
++
++ static final long MAX_VALUE = (1L << (VALUE_LEN - 1)) - 1;
++ static final long MIN_VALUE = -(1L << (VALUE_LEN - 1));
++
++ static final int MAX_SCALE = (1 << (SCALE_LEN - 1)) - 1;
++ static final int MIN_SCALE = -(1 << (SCALE_LEN - 1));
++
++ static final BigInteger MAX_I = BigInteger.valueOf(MAX_VALUE);
++ static final BigInteger MIN_I = BigInteger.valueOf(MIN_VALUE);
++
++ // Bits counts
++ static private int SCALE_LO = 56 - SCALE_LEN;
++ static private int SCALE_HI = 56; // Exclusive
++ // index
++
++ static private int VALUE_LO = 0;
++ static private int VALUE_HI = VALUE_LO + VALUE_LEN;
++
++ private int scale;
++ private long value;
++
++ public static DecimalNode56 valueOf(BigDecimal decimal) {
++ int scale = decimal.scale();
++ BigInteger bigInt = decimal.unscaledValue();
++
++ //decimal.longValueExact(); // Throws exception
++ //new BigDecimal(long);
++
++ if ( bigInt.compareTo(MAX_I) > 0 || bigInt.compareTo(MIN_I) < 0 )
++ // This check makes sure that bigInt.longValue() is safe
++ return null;
++ return valueOf(bigInt.longValue(), scale);
++ }
++
++ public static DecimalNode56 valueOf(long binValue, int scale) {
++ if ( scale < MIN_SCALE || scale > MAX_SCALE ) {
++ // log.warn("Scale out of range: ("+binValue+","+scale+")") ;
++ return null;
++ }
++
++ if ( binValue < MIN_VALUE || binValue > MAX_VALUE ) {
++ // log.warn("Value out of range: ("+binValue+","+scale+")") ;
++ return null;
++ }
++
++ return new DecimalNode56(binValue, scale);
++ }
++
++ private DecimalNode56(long value, int scale) {
++ this.scale = scale;
++ this.value = value;
++ }
++
++ public long pack() {
++ return pack(value, scale);
++ }
++
++ /** Create the long value */
++ public static long pack(long value, int scale) {
++ // pack : scale, value
++ long v = 0;
++ v = BitsLong.pack(0L, scale, SCALE_LO, SCALE_HI);
++ v = BitsLong.pack(v, value, VALUE_LO, VALUE_HI);
++ // No need to do something about negative numbers
++ return v;
++ }
++
++ public static DecimalNode56 unpack(long v) {
++ int scale = (int)BitsLong.unpack(v, SCALE_LO, SCALE_HI);
++ long value = BitsLong.unpack(v, VALUE_LO, VALUE_HI);
++ return new DecimalNode56(value, scale);
++ }
++
++ public static BigDecimal unpackAsBigDecimal(long v) {
++ int scale = (int)BitsLong.unpack(v, SCALE_LO, SCALE_HI);
++ long value = BitsLong.unpack(v, VALUE_LO, VALUE_HI);
++ // Sign extend value.
++ if ( BitsLong.isSet(value, VALUE_HI - 1) )
++ value = value | -1L << (VALUE_HI);
++ return BigDecimal.valueOf(value, scale);
++ }
++
++ public BigDecimal get() {
++ if ( decimal == null )
++ decimal = BigDecimal.valueOf(value, scale);
++ return decimal;
++ }
++
++ @Override
++ public String toString() {
++ return get().toPlainString();
++ }
++
++ public int getScale() {
++ return scale;
++ }
++
++ public long getValue() {
++ return value;
++ }
++}