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.modeshape.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.modeshape.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.api.models.FedoraResource; 038import org.fcrepo.kernel.modeshape.rdf.converters.PropertyConverter; 039import org.fcrepo.kernel.modeshape.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 the graph subjects 064 * @param session the session 065 */ 066 public PropertyToTriple(final Session session, final Converter<Resource, FedoraResource> graphSubjects) { 067 this.valueConverter = new ValueConverter(session, graphSubjects); 068 this.graphSubjects = nodeToResource(graphSubjects); 069 } 070 071 /** 072 * This nightmare of Java signature verbosity is a curried transformation. 073 * We want to go from an iterator of JCR {@link Property} to an iterator 074 * of RDF {@link Triple}s. An annoyance: some properties may produce several 075 * triples (multi-valued properties). So we cannot find a simple Property to 076 * Triple mapping. Instead, we wax clever and offer a function from any 077 * specific property to a new function, one that takes multiple values (such 078 * as occur in our multi-valued properties) to multiple triples. In other 079 * words, this is a function the outputs of which are functions specific to 080 * a given JCR property. Each output knows how to take any specific value of 081 * its specific property to a triple representing the fact that its specific 082 * property obtains that specific value on the node to which that property 083 * belongs. All of this is useful because with these operations represented 084 * as functions instead of ordinary methods, which may have side-effects, we 085 * can use efficient machinery to manipulate iterators of the objects in 086 * which we are interested, and that's exactly what we want to do in this 087 * class. See {@link org.fcrepo.kernel.modeshape.rdf.impl.PropertiesRdfContext#triplesFromProperties} for an 088 * example of the use of this class with ZippingIterator. 089 * 090 * @see <a href="http://en.wikipedia.org/wiki/Currying">Currying</a> 091 */ 092 @Override 093 public Iterator<Triple> apply(final Property p) { 094 return Iterators.transform(new PropertyValueIterator(p), new Function<Value, Triple>() { 095 096 @Override 097 public Triple apply(final Value v) { 098 return propertyvalue2triple(p, v); 099 } 100 }); 101 } 102 103 /** 104 * @param p A JCR {@link Property} 105 * @param v The {@link Value} of that Property to use (in the case of 106 * multi-valued properties) For single valued properties this 107 * must be that single value. 108 * @return An RDF {@link Triple} representing that property. 109 */ 110 private Triple propertyvalue2triple(final Property p, final Value v) { 111 LOGGER.trace("Rendering triple for Property: {} with Value: {}", p, v); 112 try { 113 114 final Triple triple = create(graphSubjects.convert(p.getParent()).asNode(), 115 propertyConverter.convert(p).asNode(), 116 convertObject(p, v)); 117 118 LOGGER.trace("Created triple: {} ", triple); 119 return triple; 120 } catch (final RepositoryException e) { 121 throw propagate(e); 122 } 123 } 124 125 private com.hp.hpl.jena.graph.Node convertObject(final Property p, final Value v) throws RepositoryException { 126 final com.hp.hpl.jena.graph.Node object = valueConverter.convert(v).asNode(); 127 128 if (object.isLiteral()) { 129 final String propertyName = p.getName(); 130 final int i = propertyName.indexOf('@'); 131 132 if (i > 0) { 133 final LiteralLabel literal = object.getLiteral(); 134 final String datatypeURI = literal.getDatatypeURI(); 135 136 if (datatypeURI.isEmpty() || datatypeURI.equals(XSDDatatype.XSDstring.getURI())) { 137 138 final String lang = propertyName.substring(i + 1); 139 return createLiteral(literal.getLexicalForm(), lang, literal.getDatatype()); 140 } 141 } 142 } 143 144 return object; 145 } 146 147}