| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.jena.riot.lang; |
| import java.util.HashMap ; |
| import java.util.Map ; |
| import java.util.UUID; |
| |
| import org.apache.jena.graph.Node ; |
| import org.apache.jena.riot.out.NodeFmtLib ; |
| import org.apache.jena.riot.out.NodeToLabel ; |
| import org.apache.jena.riot.system.MapWithScope ; |
| import org.apache.jena.riot.system.SyntaxLabels ; |
| |
| /** Allocation Nodes (Bnodes usually) based on the graph and label |
| * Various different policies. |
| * See {@link SyntaxLabels#createLabelToNode} for getting a default setup; |
| * some of the others are for testing and debugging and may not generate |
| * legal RDF overall (e.g. reparsing the same file gets the same bNodes) |
| */ |
| |
| public class LabelToNode extends MapWithScope<String, Node, Node> |
| { |
| /** Allocation from a single scope; just the label matters. |
| * This is the RDF syntax correct policy. |
| */ |
| public static LabelToNode createScopeByDocumentHash() |
| { return new LabelToNode(new AllocScopePolicy(), nodeAllocatorHash()) ; } |
| |
| /** |
| * Allocation from a single scope; just the label matters. Use this policy if repeated runs must give identical allocations |
| * @param seed Seed |
| */ |
| public static LabelToNode createScopeByDocumentHash(UUID seed) |
| { return new LabelToNode(new AllocScopePolicy(), nodeAllocatorHash(seed)) ; } |
| |
| /** The policy up to jena 2.10.0 - problems at very large scale */ |
| public static LabelToNode createScopeGlobal() |
| { return new LabelToNode(new SingleScopePolicy(), nodeAllocatorGlobal()) ; } |
| |
| /** Allocation scoped by graph and label. */ |
| public static LabelToNode createScopeByGraph() { |
| // Not AllocNodeHash here - that is unique per run, not per graph-in-run. |
| // Must use an unconditionally unique allocator. |
| return new LabelToNode(new GraphScopePolicy(), nodeAllocatorByGraph()) ; |
| } |
| |
| /** Allocation using syntax label; output is unsafe for reading (use |
| * {@link #createUseLabelEncoded()} for output-input). |
| * |
| * The reverse operation is provided by {@link NodeToLabel#createBNodeByLabelAsGiven()} |
| * but the pair is <em>unsafe</em> for output-input. Use encoded labels for that. |
| * |
| * The main purpose of this LabelToNode is to preserve the used label for debugging. |
| */ |
| public static LabelToNode createUseLabelAsGiven() |
| { return new LabelToNode(new AllocScopePolicy(), nodeAllocatorRawLabel()) ; } |
| |
| /** Allocation using an encoded syntax label |
| * (i.e. _:B<encoded> format from {@link NodeFmtLib#encodeBNodeLabel}). |
| * |
| * The reverse operation is provided by {@link NodeToLabel#createBNodeByLabelEncoded()}. |
| * This pair should be used to write out and recover blank node by internal id. |
| */ |
| public static LabelToNode createUseLabelEncoded() |
| { return new LabelToNode(new AllocScopePolicy(), nodeAllocatorEncoded()) ; } |
| |
| /** Allocation, globally scoped, that uses a incrementing field to create new nodes */ |
| public static LabelToNode createIncremental() |
| { return new LabelToNode(new SingleScopePolicy(), nodeAllocatorDeterministic()) ; } |
| |
| // ---- Create fresh allocators per call |
| |
| // The preferred node allocator - completely scalable. |
| // Nodes are unique-per-run. |
| |
| private static Allocator<String, Node, Node> nodeAllocatorHash() { |
| return new Alloc(new BlankNodeAllocatorHash()) ; |
| } |
| |
| private static Allocator<String, Node, Node> nodeAllocatorHash(UUID seed) { |
| return new Alloc(new BlankNodeAllocatorFixedSeedHash(seed)) ; |
| } |
| |
| private static Allocator<String, Node, Node> nodeAllocatorDeterministic() { |
| return new Alloc(new BlankNodeAllocatorLabel()) ; |
| } |
| |
| private static Allocator<String, Node, Node> nodeAllocatorGlobal() { |
| return new Alloc(new BlankNodeAllocatorGlobal()) ; |
| } |
| |
| private static Allocator<String, Node, Node> nodeAllocatorEncoded() { |
| return new Alloc(new BlankNodeAllocatorLabelEncoded()) ; |
| } |
| |
| private static Allocator<String, Node, Node> nodeAllocatorRawLabel() { |
| return new Alloc(new BlankNodeAllocatorLabel()) ; |
| } |
| |
| private static Allocator<String, Node, Node> nodeAllocatorByGraph() { |
| return new AllocByGraph() ; |
| } |
| |
| // ---- The class |
| |
| public LabelToNode(ScopePolicy<String, Node, Node> scopePolicy, Allocator<String, Node, Node> allocator) |
| { |
| super(scopePolicy, allocator) ; |
| } |
| |
| // ======== Scope Policies |
| |
| /** Single scope */ |
| private static class SingleScopePolicy implements ScopePolicy<String, Node, Node> |
| { |
| private Map<String, Node> map = new HashMap<>() ; |
| @Override |
| public Map<String, Node> getScope(Node scope) { return map ; } |
| @Override |
| public void clear() { map.clear(); } |
| } |
| |
| /** One scope for labels per graph */ |
| private static class GraphScopePolicy implements ScopePolicy<String, Node, Node> |
| { |
| private Map<String, Node> dftMap = new HashMap<>() ; |
| private Map<Node, Map<String, Node>> map = new HashMap<>() ; |
| @Override |
| public Map<String, Node> getScope(Node scope) |
| { |
| if ( scope == null ) |
| return dftMap ; |
| |
| Map<String, Node> x = map.get(scope) ; |
| if ( x == null ) |
| { |
| x = new HashMap<>() ; |
| map.put(scope, x) ; |
| } |
| return x ; |
| } |
| @Override |
| public void clear() { |
| dftMap.clear() ; |
| map.clear(); } |
| } |
| |
| /** No scope - use raw allocator */ |
| private static class AllocScopePolicy implements ScopePolicy<String, Node, Node> |
| { |
| @Override |
| public Map<String, Node> getScope(Node scope) { return null ; } |
| @Override |
| public void clear() { } |
| } |
| |
| |
| // ======== Node Allocators |
| // Adapter class from MapWithScope.Allocator to BlankNodeAllocator |
| // This does not take scope into account when creating nodes. |
| |
| private static class Alloc implements Allocator<String, Node, Node> { |
| final BlankNodeAllocator alloc ; |
| |
| Alloc(BlankNodeAllocator alloc) { this.alloc = alloc ; } |
| |
| @Override |
| public Node alloc(Node scope, String label) { return alloc.alloc(label) ; } |
| |
| @Override |
| public Node create() { return alloc.create() ; } |
| |
| @Override |
| public void reset() { alloc.reset() ; } |
| } |
| |
| /** Allocate a fresh blank node each time. */ |
| private static class AllocByGraph implements Allocator<String, Node, Node> { |
| BlankNodeAllocator dft = make() ; |
| Map<Node, BlankNodeAllocator> graphs = new HashMap<>() ; |
| |
| @Override public Node alloc(Node scope, String label) |
| { |
| if ( scope == null) |
| return dft.alloc(label) ; |
| |
| BlankNodeAllocator alloc = graphs.get(scope) ; |
| if ( alloc == null ) { |
| alloc = make() ; |
| graphs.put(scope, alloc) ; |
| } |
| return alloc.alloc(label) ; |
| } |
| |
| @Override public Node create() { return dft.create() ; } |
| @Override public void reset() |
| { |
| graphs.clear() ; |
| dft.reset() ; |
| } |
| |
| private BlankNodeAllocator make() { return new BlankNodeAllocatorHash() ; } |
| } |
| |
| |
| // TODO Switch to BlankNodeAllocator and a single wrapper. |
| // variables if the allocator is reusable across runs |
| // classes if a new one is needed each time. |
| // Shared ones must be thread-safe. |
| |
| |
| // /** Allocate bnode using a per-run seed and the label presented. |
| // * This is the most scalable, always legal allocator. |
| // * Not thread safe - not reusable. |
| // * Create a new allocator for each parser run. |
| // */ |
| // private static class AllocNodeHash implements Allocator<String, Node> { |
| // private BlankNodeAllocator alloc = new BlankNodeAllocatorHash() ; |
| // |
| // @Override public Node alloc(String label) { return alloc.alloc(label) ; } |
| // @Override public Node create() { return alloc.create() ; } |
| // @Override public void reset() { alloc.reset() ; } |
| // } ; |
| // |
| // |
| // |
| // /** Allocate bnodes using an incremental counter - deterministic for parsing a given file */ |
| // private static Allocator<String, Node> nodeMakerDeterministic = new Allocator<String, Node>() |
| // { |
| // private AtomicLong counter = new AtomicLong(0) ; |
| // |
| // @Override |
| // public Node alloc(Node scope, String label) { |
| // return create() ; |
| // } |
| // |
| // @Override |
| // public Node create() { |
| // String $ = format("B0x%04X", counter.incrementAndGet()) ; |
| // return NodeFactory.createAnon(new AnonId($)) ; |
| // } |
| // |
| // @Override |
| // public void reset() {} |
| // } ; |
| // |
| // private static Allocator<String, Node> nodeMakerByLabel = new Allocator<String, Node>() |
| // { |
| // private AtomicLong counter = new AtomicLong(0) ; |
| // |
| // @Override |
| // public Node alloc(String label) |
| // { |
| // return NodeFactory.createAnon(new AnonId(label)) ; |
| // } |
| // |
| // @Override public Node create() { return alloc(null, SysRIOT.BNodeGenIdPrefix+(counter.getAndIncrement())) ; } |
| // |
| // @Override |
| // public void reset() {} |
| // } ; |
| // |
| // private static Allocator<String, Node> nodeMakerByLabelEncoded = new Allocator<String, Node>() |
| // { |
| // private AtomicLong counter = new AtomicLong(0) ; |
| // |
| // @Override |
| // public Node alloc(Node scope, String label) |
| // { |
| // return NodeFactory.createAnon(new AnonId(NodeFmtLib.decodeBNodeLabel(label))) ; |
| // } |
| // |
| // @Override public Node create() { return alloc(SysRIOT.BNodeGenIdPrefix+(counter.getAndIncrement())) ; } |
| // @Override |
| // |
| // public void reset() {} |
| // } ; |
| } |