001/* 002 * Licensed to DuraSpace under one or more contributor license agreements. 003 * See the NOTICE file distributed with this work for additional information 004 * regarding copyright ownership. 005 * 006 * DuraSpace licenses this file to you under the Apache License, 007 * Version 2.0 (the "License"); you may not use this file except in 008 * compliance with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.fcrepo.kernel.modeshape.rdf.impl.mappings; 019 020import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDstring; 021import static org.apache.jena.graph.NodeFactory.createLiteral; 022import static org.apache.jena.graph.Triple.create; 023import static org.fcrepo.kernel.modeshape.identifiers.NodeResourceConverter.nodeToResource; 024import static org.fcrepo.kernel.modeshape.utils.StreamUtils.iteratorToStream; 025import static org.slf4j.LoggerFactory.getLogger; 026 027import java.util.function.Function; 028import java.util.stream.Stream; 029 030import javax.jcr.Node; 031import javax.jcr.Property; 032import javax.jcr.RepositoryException; 033import javax.jcr.Session; 034import javax.jcr.Value; 035 036import com.google.common.base.Converter; 037import org.apache.jena.graph.impl.LiteralLabel; 038import org.apache.jena.rdf.model.Resource; 039 040import org.fcrepo.kernel.api.exception.RepositoryRuntimeException; 041import org.fcrepo.kernel.api.models.FedoraResource; 042import org.fcrepo.kernel.modeshape.rdf.converters.PropertyConverter; 043import org.fcrepo.kernel.modeshape.rdf.converters.ValueConverter; 044import org.apache.jena.datatypes.RDFDatatype; 045import org.apache.jena.graph.Triple; 046import org.slf4j.Logger; 047 048/** 049 * Utility for moving from JCR properties to RDF triples. 050 * 051 * @author ajs6f 052 * @since Oct 10, 2013 053 */ 054public class PropertyToTriple implements Function<Property, Stream<Triple>> { 055 056 private static final Logger LOGGER = getLogger(PropertyToTriple.class); 057 058 private static final PropertyConverter propertyConverter = new PropertyConverter(); 059 private final ValueConverter valueConverter; 060 private final Converter<Node, Resource> translator; 061 062 /** 063 * Default constructor. We require a {@link Converter} in order to construct the RDF subjects of our triples. 064 * 065 * @param converter a converter between RDF and the Fedora model 066 * @param session the JCR session 067 */ 068 public PropertyToTriple(final Session session, final Converter<Resource, FedoraResource> converter) { 069 this.valueConverter = new ValueConverter(session, converter); 070 this.translator = nodeToResource(converter); 071 } 072 073 @Override 074 public Stream<Triple> apply(final Property p) { 075 try { 076 final org.apache.jena.graph.Node subject = translator.convert(p.getParent()).asNode(); 077 final org.apache.jena.graph.Node propPredicate = propertyConverter.convert(p).asNode(); 078 final String propertyName = p.getName(); 079 080 return iteratorToStream(new PropertyValueIterator(p)).filter(this::valueCanBeConverted).map(v -> { 081 final org.apache.jena.graph.Node object = valueConverter.convert(v).asNode(); 082 if (object.isLiteral()) { 083 // unpack the name of the property for information about what kind of literal 084 final int i = propertyName.indexOf('@'); 085 if (i > 0) { 086 final LiteralLabel literal = object.getLiteral(); 087 final RDFDatatype datatype = literal.getDatatype(); 088 final String datatypeURI = datatype.getURI(); 089 if (datatypeURI.isEmpty() || datatype.equals(XSDstring)) { 090 // this is an RDF string literal and could involve an RDF lang tag 091 final String lang = propertyName.substring(i + 1); 092 final String lex = literal.getLexicalForm(); 093 return create(subject, propPredicate, createLiteral(lex, lang, datatype)); 094 } 095 } 096 } 097 return create(subject, propPredicate, object); 098 }); 099 } catch (final RepositoryException e) { 100 throw new RepositoryRuntimeException(e); 101 } 102 } 103 104 /** 105 * This method tests if a given value can be converted. 106 * The scenario when this may not be true is for (weak)reference properties that target an non-existent resource. 107 * This scenario generally should not be possible, but the following bug introduced the possibility: 108 * https://jira.duraspace.org/browse/FCREPO-2323 109 * 110 * @param value to be tested for whether it can be converted to an RDFNode or not 111 * @return true if value can be converted 112 */ 113 private boolean valueCanBeConverted(final Value value) { 114 try { 115 valueConverter.convert(value); 116 return true; 117 } catch (final RepositoryRuntimeException e) { 118 LOGGER.warn("Reference to non-existent resource encounterd: {}", value); 119 return false; 120 } 121 }; 122}