001/** 002 * Copyright 2015 DuraSpace, Inc. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.fcrepo.kernel.impl.rdf.impl.mappings; 017 018import static com.google.common.base.Throwables.propagate; 019import static com.hp.hpl.jena.graph.NodeFactory.createLiteral; 020import static com.hp.hpl.jena.graph.Triple.create; 021import static org.fcrepo.kernel.impl.identifiers.NodeResourceConverter.nodeToResource; 022import static org.slf4j.LoggerFactory.getLogger; 023 024import java.util.Iterator; 025 026import javax.jcr.Node; 027import javax.jcr.Property; 028import javax.jcr.RepositoryException; 029import javax.jcr.Session; 030import javax.jcr.Value; 031 032import com.google.common.base.Converter; 033import com.google.common.collect.Iterators; 034import com.hp.hpl.jena.datatypes.xsd.XSDDatatype; 035import com.hp.hpl.jena.graph.impl.LiteralLabel; 036import com.hp.hpl.jena.rdf.model.Resource; 037import org.fcrepo.kernel.models.FedoraResource; 038import org.fcrepo.kernel.impl.rdf.converters.PropertyConverter; 039import org.fcrepo.kernel.impl.rdf.converters.ValueConverter; 040import org.slf4j.Logger; 041import com.google.common.base.Function; 042import com.hp.hpl.jena.graph.Triple; 043 044/** 045 * Utility for moving from JCR properties to RDF triples. 046 * 047 * @author ajs6f 048 * @since Oct 10, 2013 049 */ 050public class PropertyToTriple implements 051 Function<Property, Iterator<Triple>> { 052 053 private static final PropertyConverter propertyConverter = new PropertyConverter(); 054 private final ValueConverter valueConverter; 055 private Converter<Node, Resource> graphSubjects; 056 057 private static final Logger LOGGER = getLogger(PropertyToTriple.class); 058 059 /** 060 * Default constructor. We require a {@link Converter} in order to 061 * construct the externally-meaningful RDF subjects of our triples. 062 * 063 * @param graphSubjects 064 */ 065 public PropertyToTriple(final Session session, final Converter<Resource, FedoraResource> graphSubjects) { 066 this.valueConverter = new ValueConverter(session, graphSubjects); 067 this.graphSubjects = nodeToResource(graphSubjects); 068 } 069 070 /** 071 * This nightmare of Java signature verbosity is a curried transformation. 072 * We want to go from an iterator of JCR {@link Property} to an iterator 073 * of RDF {@link Triple}s. An annoyance: some properties may produce several 074 * triples (multi-valued properties). So we cannot find a simple Property to 075 * Triple mapping. Instead, we wax clever and offer a function from any 076 * specific property to a new function, one that takes multiple values (such 077 * as occur in our multi-valued properties) to multiple triples. In other 078 * words, this is a function the outputs of which are functions specific to 079 * a given JCR property. Each output knows how to take any specific value of 080 * its specific property to a triple representing the fact that its specific 081 * property obtains that specific value on the node to which that property 082 * belongs. All of this is useful because with these operations represented 083 * as functions instead of ordinary methods, which may have side-effects, we 084 * can use efficient machinery to manipulate iterators of the objects in 085 * which we are interested, and that's exactly what we want to do in this 086 * class. See {@link org.fcrepo.kernel.impl.rdf.impl.PropertiesRdfContext#triplesFromProperties} for an 087 * example of the use of this class with {@link ZippingIterator}. 088 * 089 * @see <a href="http://en.wikipedia.org/wiki/Currying">Currying</a> 090 */ 091 @Override 092 public Iterator<Triple> apply(final Property p) { 093 return Iterators.transform(new PropertyValueIterator(p), new Function<Value, Triple>() { 094 095 @Override 096 public Triple apply(final Value v) { 097 return propertyvalue2triple(p, v); 098 } 099 }); 100 } 101 102 /** 103 * @param p A JCR {@link Property} 104 * @param v The {@link Value} of that Property to use (in the case of 105 * multi-valued properties) For single valued properties this 106 * must be that single value. 107 * @return An RDF {@link Triple} representing that property. 108 */ 109 private Triple propertyvalue2triple(final Property p, final Value v) { 110 LOGGER.trace("Rendering triple for Property: {} with Value: {}", p, v); 111 try { 112 113 final Triple triple = create(graphSubjects.convert(p.getParent()).asNode(), 114 propertyConverter.convert(p).asNode(), 115 convertObject(p, v)); 116 117 LOGGER.trace("Created triple: {} ", triple); 118 return triple; 119 } catch (final RepositoryException e) { 120 throw propagate(e); 121 } 122 } 123 124 private com.hp.hpl.jena.graph.Node convertObject(final Property p, final Value v) throws RepositoryException { 125 final com.hp.hpl.jena.graph.Node object = valueConverter.convert(v).asNode(); 126 127 if (object.isLiteral()) { 128 final String propertyName = p.getName(); 129 final int i = propertyName.indexOf("@"); 130 131 if (i > 0) { 132 final LiteralLabel literal = object.getLiteral(); 133 final String datatypeURI = literal.getDatatypeURI(); 134 135 if (datatypeURI.isEmpty() || datatypeURI.equals(XSDDatatype.XSDstring.getURI())) { 136 137 final String lang = propertyName.substring(i + 1); 138 return createLiteral(literal.getLexicalForm(), lang, literal.getDatatype()); 139 } 140 } 141 } 142 143 return object; 144 } 145 146}