Java tutorial
/* * 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.marmotta.platform.ldp.patch; import org.apache.commons.io.IOUtils; import org.apache.marmotta.platform.ldp.patch.model.PatchLine; import org.apache.marmotta.platform.ldp.patch.model.WildcardStatement; import org.apache.marmotta.platform.ldp.patch.parser.ParseException; import org.apache.marmotta.platform.ldp.patch.parser.RdfPatchParser; import org.apache.marmotta.platform.ldp.patch.parser.RdfPatchParserImpl; import org.openrdf.model.*; import org.openrdf.repository.Repository; import org.openrdf.repository.RepositoryConnection; import org.openrdf.repository.RepositoryException; import org.openrdf.repository.RepositoryResult; import java.io.IOException; import java.io.InputStream; import java.util.*; /** * RdfPatchUtil - Util-Class to apply and create rdf-patches on a {@link Repository} or {@link org.openrdf.repository.RepositoryConnection} * * @author Jakob Frank */ public class RdfPatchUtil { /** * Apply the provided patch to the repository * @param repository the {@link org.openrdf.repository.Repository} to patch * @param patch the patch to apply * @param contexts restrict changes to these contexts (leave empty to apply to <em>all</em> contexts) * @throws ParseException if the patch could not be parsed * @throws InvalidPatchDocumentException if the patch is invalid */ public static void applyPatch(Repository repository, String patch, Resource... contexts) throws RepositoryException, ParseException, InvalidPatchDocumentException { applyPatch(repository, getPatch(repository.getValueFactory(), patch), contexts); } /** * Apply the provided patch to the repository * @param repository the {@link org.openrdf.repository.Repository} to patch * @param patchSource the patch to apply * @param contexts restrict changes to these contexts (leave empty to apply to <em>all</em> contexts) * @throws ParseException if the patch could not be parsed * @throws InvalidPatchDocumentException if the patch is invalid */ public static void applyPatch(Repository repository, InputStream patchSource, Resource... contexts) throws RepositoryException, ParseException, InvalidPatchDocumentException { applyPatch(repository, getPatch(repository.getValueFactory(), patchSource), contexts); } /** * Apply the provided patch to the repository * @param connection the {@link org.openrdf.repository.RepositoryConnection} to patch * @param patch the patch to apply * @param contexts restrict changes to these contexts (leave empty to apply to <em>all</em> contexts) * @throws ParseException if the patch could not be parsed * @throws InvalidPatchDocumentException if the patch is invalid */ public static void applyPatch(RepositoryConnection connection, String patch, Resource... contexts) throws RepositoryException, ParseException, InvalidPatchDocumentException { applyPatch(connection, getPatch(connection.getValueFactory(), patch), contexts); } /** * Apply the provided patch to the repository * @param connection the {@link org.openrdf.repository.RepositoryConnection} to patch * @param patchSource the patch to apply * @param contexts restrict changes to these contexts (leave empty to apply to <em>all</em> contexts) * @throws ParseException if the patch could not be parsed * @throws InvalidPatchDocumentException if the patch is invalid */ public static void applyPatch(RepositoryConnection connection, InputStream patchSource, Resource... contexts) throws RepositoryException, ParseException, InvalidPatchDocumentException { applyPatch(connection, getPatch(connection.getValueFactory(), patchSource), contexts); } /** * Apply the provided patch to the repository * @param repository the {@link org.openrdf.repository.Repository} to patch * @param patch the patch to apply * @param contexts restrict changes to these contexts (leave empty to apply to <em>all</em> contexts) * @throws InvalidPatchDocumentException if the patch is invalid */ public static void applyPatch(Repository repository, List<PatchLine> patch, Resource... contexts) throws RepositoryException, InvalidPatchDocumentException { RepositoryConnection con = repository.getConnection(); try { con.begin(); applyPatch(con, patch, contexts); con.commit(); } catch (final Throwable t) { con.rollback(); throw t; } finally { con.close(); } } /** * Apply the provided patch to the repository * @param connection the {@link org.openrdf.repository.RepositoryConnection} to patch * @param patch the patch to apply * @param contexts restrict changes to these contexts (leave empty to apply to <em>all</em> contexts) * @throws InvalidPatchDocumentException if the patch is invalid */ public static void applyPatch(RepositoryConnection connection, List<PatchLine> patch, Resource... contexts) throws RepositoryException, InvalidPatchDocumentException { Resource subject = null; URI predicate = null; Value object = null; for (PatchLine patchLine : patch) { final Statement statement = patchLine.getStatement(); subject = statement.getSubject() != null ? statement.getSubject() : subject; predicate = statement.getPredicate() != null ? statement.getPredicate() : predicate; object = statement.getObject() != null ? statement.getObject() : object; if (subject == null || predicate == null || object == null) { if (subject == null) { throw new InvalidPatchDocumentException("Cannot resolve 'R' - subject was never set"); } if (predicate == null) { throw new InvalidPatchDocumentException("Cannot resolve 'R' - predicate was never set"); } if (object == null) { throw new InvalidPatchDocumentException("Cannot resolve 'R' - object was never set"); } } switch (patchLine.getOperator()) { case ADD: connection.add(subject, predicate, object, contexts); break; case DELETE: connection.remove(subject, predicate, object, contexts); break; default: throw new IllegalArgumentException("Unknown patch operation: " + patchLine.getOperator()); } } } private static List<PatchLine> getPatch(ValueFactory valueFactory, InputStream is) throws ParseException { RdfPatchParser parser = new RdfPatchParserImpl(is); parser.setValueFactory(valueFactory); return parser.parsePatch(); } private static List<PatchLine> getPatch(ValueFactory valueFactory, String patch) throws ParseException { try (InputStream is = IOUtils.toInputStream(patch)) { return getPatch(valueFactory, is); } catch (IOException e) { // You can always close an InputStream on a String assert (false); return null; } } /** * Create an RDF-Patch that applies the changes from {@code r1} to {@code r2}. * @param r1 the 'from' Repository * @param r2 the 'to' Repository * @param optimize optimize the patch, i.e. remove duplicate or idempotent operations. * @param contexts restrict analysis to these contexts (leave empty to use <em>all</em> contexts) * @return List of PatchLines */ public static List<PatchLine> diff(Repository r1, Repository r2, boolean optimize, Resource... contexts) throws RepositoryException { final RepositoryConnection c1 = r1.getConnection(), c2 = r2.getConnection(); try { c1.begin(); c2.begin(); final List<PatchLine> diff = diff(c1, c2, optimize, contexts); c1.commit(); c2.commit(); return diff; } finally { c1.close(); c2.close(); } } /** * Create an RDF-Patch that applies the changes from {@code c1} to {@code c2}. * @param c1 the 'from' RepositoryConnection * @param c2 the 'to' RepositoryConnection * @param optimize optimize the patch, i.e. remove duplicate or idempotent operations. * @param contexts restrict analysis to these contexts (leave empty to use <em>all</em> contexts) * @return List of PatchLines */ public static List<PatchLine> diff(RepositoryConnection c1, RepositoryConnection c2, boolean optimize, Resource... contexts) throws RepositoryException { Set<Statement> additions = new HashSet<>(), removals = new HashSet<>(); final RepositoryResult<Statement> st1 = c1.getStatements(null, null, null, false, contexts); try { while (st1.hasNext()) { final Statement st = st1.next(); if (!c2.hasStatement(st, false, contexts)) { removals.add(st); } } } finally { st1.close(); } final RepositoryResult<Statement> st2 = c2.getStatements(null, null, null, false, contexts); try { while (st2.hasNext()) { final Statement st = st2.next(); if (!c1.hasStatement(st, false, contexts)) { additions.add(st); } } } finally { st2.close(); } if (optimize) { final TreeSet<Statement> delList = new TreeSet<>(new StatementComparator()); delList.addAll(removals); final TreeSet<Statement> addList = new TreeSet<>(new StatementComparator()); addList.addAll(additions); additions = addList; removals = delList; } Resource pS = null; URI pP = null; Value pO = null; ArrayList<PatchLine> patch = new ArrayList<>(removals.size() + additions.size()); for (Statement s : removals) { final WildcardStatement ws = new WildcardStatement(s.getSubject().equals(pS) ? null : s.getSubject(), s.getPredicate().equals(pP) ? null : s.getPredicate(), s.getObject().equals(pO) ? null : s.getObject()); patch.add(new PatchLine(PatchLine.Operator.DELETE, ws)); pS = s.getSubject(); pP = s.getPredicate(); pO = s.getObject(); } for (Statement s : additions) { final WildcardStatement ws = new WildcardStatement(s.getSubject().equals(pS) ? null : s.getSubject(), s.getPredicate().equals(pP) ? null : s.getPredicate(), s.getObject().equals(pO) ? null : s.getObject()); patch.add(new PatchLine(PatchLine.Operator.ADD, ws)); pS = s.getSubject(); pP = s.getPredicate(); pO = s.getObject(); } return patch; } private static class StatementComparator implements Comparator<Statement> { @Override public int compare(Statement s1, Statement s2) { final int si = compare(s1.getSubject(), s2.getSubject()); if (si != 0) { return si; } else { final int pi = compare(s1.getPredicate(), s2.getPredicate()); if (pi != 0) { return pi; } else { return compare(s1.getObject(), s2.getObject()); } } } private int compare(Value v1, Value v2) { return v1.toString().compareTo(v2.toString()); } } private RdfPatchUtil() { // static access only } }