/* * Licensed to DuraSpace under one or more contributor license agreements. * See the NOTICE file distributed with this work for additional information * regarding copyright ownership. * * DuraSpace 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.fcrepo.kernel.modeshape.rdf.impl.mappings; import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDstring; import static org.apache.jena.graph.NodeFactory.createLiteral; import static org.apache.jena.graph.Triple.create; import static org.fcrepo.kernel.modeshape.identifiers.NodeResourceConverter.nodeToResource; import static org.fcrepo.kernel.modeshape.utils.StreamUtils.iteratorToStream; import static org.slf4j.LoggerFactory.getLogger; import java.util.function.Function; import java.util.stream.Stream; import javax.jcr.Node; import javax.jcr.Property; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.Value; import com.google.common.base.Converter; import org.apache.jena.graph.impl.LiteralLabel; import org.apache.jena.rdf.model.Resource; import org.fcrepo.kernel.api.exception.RepositoryRuntimeException; import org.fcrepo.kernel.api.models.FedoraResource; import org.fcrepo.kernel.modeshape.rdf.converters.PropertyConverter; import org.fcrepo.kernel.modeshape.rdf.converters.ValueConverter; import org.apache.jena.datatypes.RDFDatatype; import org.apache.jena.graph.Triple; import org.slf4j.Logger; /** * Utility for moving from JCR properties to RDF triples. * * @author ajs6f * @since Oct 10, 2013 */ public class PropertyToTriple implements Function<Property, Stream<Triple>> { private static final Logger LOGGER = getLogger(PropertyToTriple.class); private static final PropertyConverter propertyConverter = new PropertyConverter(); private final ValueConverter valueConverter; private final Converter<Node, Resource> translator; /** * Default constructor. We require a {@link Converter} in order to construct the RDF subjects of our triples. * * @param converter a converter between RDF and the Fedora model * @param session the JCR session */ public PropertyToTriple(final Session session, final Converter<Resource, FedoraResource> converter) { this.valueConverter = new ValueConverter(session, converter); this.translator = nodeToResource(converter); } @Override public Stream<Triple> apply(final Property p) { try { final org.apache.jena.graph.Node subject = translator.convert(p.getParent()).asNode(); final org.apache.jena.graph.Node propPredicate = propertyConverter.convert(p).asNode(); final String propertyName = p.getName(); return iteratorToStream(new PropertyValueIterator(p)).filter(this::valueCanBeConverted).map(v -> { final org.apache.jena.graph.Node object = valueConverter.convert(v).asNode(); if (object.isLiteral()) { // unpack the name of the property for information about what kind of literal final int i = propertyName.indexOf('@'); if (i > 0) { final LiteralLabel literal = object.getLiteral(); final RDFDatatype datatype = literal.getDatatype(); final String datatypeURI = datatype.getURI(); if (datatypeURI.isEmpty() || datatype.equals(XSDstring)) { // this is an RDF string literal and could involve an RDF lang tag final String lang = propertyName.substring(i + 1); final String lex = literal.getLexicalForm(); return create(subject, propPredicate, createLiteral(lex, lang, datatype)); } } } return create(subject, propPredicate, object); }); } catch (final RepositoryException e) { throw new RepositoryRuntimeException(e); } } /** * This method tests if a given value can be converted. * The scenario when this may not be true is for (weak)reference properties that target an non-existent resource. * This scenario generally should not be possible, but the following bug introduced the possibility: * https://jira.duraspace.org/browse/FCREPO-2323 * * @param value to be tested for whether it can be converted to an RDFNode or not * @return true if value can be converted */ private boolean valueCanBeConverted(final Value value) { try { valueConverter.convert(value); return true; } catch (final RepositoryRuntimeException e) { LOGGER.warn("Reference to non-existent resource encounterd: {}", value); return false; } }; }