All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.rdf4j.model.impl.LinkedHashModel Maven / Gradle / Ivy

There is a newer version: 5.1.0-M1
Show newest version
/*******************************************************************************
 * Copyright (c) 2015 Eclipse RDF4J contributors, Aduna, and others.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Distribution License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *******************************************************************************/
package org.eclipse.rdf4j.model.impl;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.Namespace;
import org.eclipse.rdf4j.model.Resource;
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.Value;
import org.eclipse.rdf4j.model.util.PatternIterator;

/**
 * Hash table based implementation of the {@link Model} interface.
 * 

* This implementation provides constant-time performance for filters using a single term, assuming the hash function * disperses the elements properly among the buckets. Each term is indexed using a {@link HashMap}. When multiple terms * are provided in a filter the index, of the term that reduces the possible {@link Statement}s the most, is used and a * sequential scan is used to filter additional terms. *

* Note that this implementation is not synchronized. If multiple threads access a model concurrently, and at * least one of the threads modifies the model, it must be synchronized externally. This is typically accomplished by * synchronizing on some object that naturally encapsulates the model. If no such object exists, the set should be * "wrapped" using the * Models.synchronizedModel method. *

* * @author James Leigh */ @SuppressWarnings("unchecked") public class LinkedHashModel extends AbstractModel { private static final long serialVersionUID = -9161104123818983614L; static final Resource[] NULL_CTX = new Resource[] { null }; final Set namespaces = new LinkedHashSet<>(); transient Map values; transient Set statements; public LinkedHashModel() { this(128); } public LinkedHashModel(Model model) { this(model.getNamespaces()); addAll(model); } public LinkedHashModel(Collection c) { this(c.size()); addAll(c); } public LinkedHashModel(int size) { super(); // assume fewer unique values than statements (eg. some reuse of nodes instatements) values = new HashMap<>(size, 0.75f); // compensate for the loadfactor by multiplying by 2 statements = new LinkedHashSet<>(size * 2, 0.75f); } public LinkedHashModel(Set namespaces, Collection c) { this(c); this.namespaces.addAll(namespaces); } public LinkedHashModel(Set namespaces) { this(); this.namespaces.addAll(namespaces); } public LinkedHashModel(Set namespaces, int size) { this(size); this.namespaces.addAll(namespaces); } @Override public Optional getNamespace(String prefix) { for (Namespace nextNamespace : namespaces) { if (prefix.equals(nextNamespace.getPrefix())) { return Optional.of(nextNamespace); } } return Optional.empty(); } @Override public Set getNamespaces() { return namespaces; } @Override public Namespace setNamespace(String prefix, String name) { removeNamespace(prefix); Namespace result = new SimpleNamespace(prefix, name); namespaces.add(result); return result; } @Override public void setNamespace(Namespace namespace) { removeNamespace(namespace.getPrefix()); namespaces.add(namespace); } @Override public Optional removeNamespace(String prefix) { Optional result = getNamespace(prefix); result.ifPresent(namespace -> namespaces.remove(namespace)); return result; } @Override public int size() { return statements.size(); } @Override public boolean add(Statement statement) { Resource subj = statement.getSubject(); IRI pred = statement.getPredicate(); Value obj = statement.getObject(); Resource context = statement.getContext(); ModelNode s = asNode(subj); ModelNode p = asNode(pred); ModelNode o = asNode(obj); ModelNode c = asNode(context); ModelStatement modelStatement = new ModelStatement(s, p, o, c, statement); return addModelStatement(modelStatement); } @Override public boolean add(Resource subj, IRI pred, Value obj, Resource... contexts) { if (subj == null || pred == null || obj == null) { throw new UnsupportedOperationException("Incomplete statement"); } Value[] ctxs = notNull(contexts); if (ctxs.length == 0) { ctxs = NULL_CTX; } boolean changed = false; for (Value ctx : ctxs) { ModelNode s = asNode(subj); ModelNode p = asNode(pred); ModelNode o = asNode(obj); ModelNode c = asNode((Resource) ctx); ModelStatement st = new ModelStatement(s, p, o, c); changed |= addModelStatement(st); } return changed; } @Override public void clear() { values.clear(); statements.clear(); } @Override public boolean contains(Object o) { if (o instanceof Statement) { if (o instanceof ModelStatement) { return statements.contains(o); } return find((Statement) o).hasNext(); } return false; } @Override public Iterator iterator() { return matchPattern(null, null, null); } @Override public boolean contains(Resource subj, IRI pred, Value obj, Resource... contexts) { return matchPattern(subj, pred, obj, contexts).hasNext(); } @Override public boolean remove(Resource subj, IRI pred, Value obj, Resource... contexts) { if (isEmpty()) { return false; } Iterator iter = matchPattern(subj, pred, obj, contexts); if (!iter.hasNext()) { return false; } while (iter.hasNext()) { iter.next(); iter.remove(); } return true; } @Override public Model filter(final Resource subj, final IRI pred, final Value obj, final Resource... contexts) { return new FilteredModel(this, subj, pred, obj, contexts) { private static final long serialVersionUID = 396293781006255959L; @Override public Iterator iterator() { return matchPattern(subj, pred, obj, contexts); } @Override protected void removeFilteredTermIteration(Iterator iter, Resource subj, IRI pred, Value obj, Resource... contexts) { LinkedHashModel.this.removeTermIteration(iter, subj, pred, obj, contexts); } }; } @Override public void removeTermIteration(Iterator iterator, Resource subj, IRI pred, Value obj, Resource... contexts) { Set owner = ((ModelIterator) iterator).getOwner(); Set chosen = choose(subj, pred, obj, contexts); Iterator iter = chosen.iterator(); iter = new PatternIterator<>(iter, subj, pred, obj, contexts); while (iter.hasNext()) { ModelStatement last = iter.next(); if (statements == owner) { statements = new LinkedHashSet<>(statements); statements.remove(last); } else if (statements != chosen) { statements.remove(last); } if (last.subj.subjects == owner) { last.subj.subjects = new LinkedHashSet<>(last.subj.subjects); last.subj.subjects.remove(last); } else if (last.subj.subjects != chosen) { last.subj.subjects.remove(last); } if (last.pred.predicates == owner) { last.pred.predicates = new LinkedHashSet<>(statements); last.pred.predicates.remove(last); } else if (last.pred.predicates != chosen) { last.pred.predicates.remove(last); } if (last.obj.objects == owner) { last.obj.objects = new LinkedHashSet<>(statements); last.obj.objects.remove(last); } else if (last.obj.objects != chosen) { last.obj.objects.remove(last); } if (last.ctx.contexts == owner) { last.ctx.contexts = new LinkedHashSet<>(statements); last.ctx.contexts.remove(last); } else if (last.ctx.contexts != chosen) { last.ctx.contexts.remove(last); } if (owner != chosen) { iter.remove(); // remove from chosen } } } @Override public boolean isEmpty() { return statements.isEmpty(); } private class ModelIterator implements Iterator { private final Iterator iter; private final Set owner; private ModelStatement last; public ModelIterator(Iterator iter, Set owner) { this.iter = iter; this.owner = owner; } public Set getOwner() { return owner; } @Override public boolean hasNext() { return iter.hasNext(); } @Override public ModelStatement next() { return last = iter.next(); } @Override public void remove() { if (last == null) { throw new IllegalStateException(); } removeFrom(statements); removeFrom(last.subj.subjects); removeFrom(last.pred.predicates); removeFrom(last.obj.objects); removeFrom(last.ctx.contexts); iter.remove(); // remove from owner } private void removeFrom(Set subjects) { if (subjects != owner) { subjects.remove(last); } } } private static class ModelNode implements Serializable { private static final long serialVersionUID = -1205676084606998540L; Set subjects = new LinkedHashSet<>(); Set predicates = new LinkedHashSet<>(); Set objects = new LinkedHashSet<>(); Set contexts = new LinkedHashSet<>(); private final V value; public ModelNode(V value) { this.value = value; } public V getValue() { return value; } } public static class ModelStatement extends GenericStatement { private static final long serialVersionUID = 2200404772364346279L; private Statement statement; ModelNode subj; ModelNode pred; ModelNode obj; ModelNode ctx; ModelStatement(ModelNode subj, ModelNode pred, ModelNode obj, ModelNode ctx) { super(subj.getValue(), pred.getValue(), obj.getValue(), ctx.getValue()); this.subj = subj; this.pred = pred; this.obj = obj; this.ctx = ctx; } ModelStatement(ModelNode subj, ModelNode pred, ModelNode obj, ModelNode ctx, Statement statement) { super(subj.getValue(), pred.getValue(), obj.getValue(), ctx.getValue()); this.subj = subj; this.pred = pred; this.obj = obj; this.ctx = ctx; this.statement = statement; } @Override public Resource getSubject() { return subj.getValue(); } @Override public IRI getPredicate() { return pred.getValue(); } @Override public Value getObject() { return obj.getValue(); } @Override public Resource getContext() { if (ctx == null) { return null; } return ctx.getValue(); } @Override public boolean equals(Object other) { if (this == other) { return true; } if (!super.equals(other)) { return false; } if (getContext() == null) { return ((Statement) other).getContext() == null; } return getContext().equals(((Statement) other).getContext()); } public Statement getStatement() { return statement; } } private void writeObject(ObjectOutputStream s) throws IOException { // Write out any hidden serialization magic s.defaultWriteObject(); // Write in size s.writeInt(statements.size()); // Write in all elements for (ModelStatement st : statements) { Resource subj = st.getSubject(); IRI pred = st.getPredicate(); Value obj = st.getObject(); Resource ctx = st.getContext(); s.writeObject(new GenericStatement<>(subj, pred, obj, ctx)); } } private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { // Read in any hidden serialization magic s.defaultReadObject(); // Read in size int size = s.readInt(); values = new HashMap<>(size * 2); statements = new LinkedHashSet<>(size); // Read in all elements for (int i = 0; i < size; i++) { Statement st = (Statement) s.readObject(); add(st); } } private ModelIterator matchPattern(Resource subj, IRI pred, Value obj, Resource... contexts) { Set set = choose(subj, pred, obj, contexts); Iterator it = set.iterator(); Iterator iter; iter = new PatternIterator<>(it, subj, pred, obj, contexts); return new ModelIterator(iter, set); } private Set choose(Resource subj, IRI pred, Value obj, Resource... contexts) { contexts = notNull(contexts); Set s = null; Set p = null; Set o = null; if (subj != null) { ModelNode modelNode = values.get(subj); if (modelNode == null) { return Collections.emptySet(); } s = modelNode.subjects; } if (pred != null) { ModelNode modelNode = values.get(pred); if (modelNode == null) { return Collections.emptySet(); } p = modelNode.predicates; } if (obj != null) { ModelNode modelNode = values.get(obj); if (modelNode == null) { return Collections.emptySet(); } o = modelNode.objects; } if (contexts.length == 1) { ModelNode modelNode = values.get(contexts[0]); if (modelNode == null) { return Collections.emptySet(); } Set c = modelNode.contexts; return smallest(statements, s, p, o, c); } else { return smallest(statements, s, p, o); } } private Resource[] notNull(Resource[] contexts) { if (contexts == null) { return new Resource[] { null }; } return contexts; } private Iterator find(Statement st) { Resource subj = st.getSubject(); IRI pred = st.getPredicate(); Value obj = st.getObject(); Resource ctx = st.getContext(); return matchPattern(subj, pred, obj, ctx); } private boolean addModelStatement(ModelStatement st) { Set subj = st.subj.subjects; Set pred = st.pred.predicates; Set obj = st.obj.objects; Set ctx = st.ctx.contexts; if (smallest(subj, pred, obj, ctx).contains(st)) { return false; } statements.add(st); subj.add(st); pred.add(st); obj.add(st); ctx.add(st); return true; } private Set smallest(Set... sets) { int minSize = Integer.MAX_VALUE; Set minSet = null; for (Set set : sets) { if (set != null && set.size() < minSize) { minSet = set; minSize = set.size(); } } return minSet; } private ModelNode asNode(V value) { ModelNode node = values.get(value); if (node != null) { return node; } node = new ModelNode<>(value); values.put(value, node); return node; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy