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.transform.transformations; 019 020import com.google.common.collect.ImmutableList; 021import com.hp.hpl.jena.rdf.model.RDFNode; 022import com.hp.hpl.jena.rdf.model.Resource; 023 024import org.apache.marmotta.ldpath.LDPath; 025import org.apache.marmotta.ldpath.backend.jena.GenericJenaBackend; 026import org.apache.marmotta.ldpath.exception.LDPathParseException; 027 028import org.fcrepo.kernel.api.RdfStream; 029import org.fcrepo.kernel.api.exception.RepositoryRuntimeException; 030import org.fcrepo.kernel.api.models.FedoraBinary; 031import org.fcrepo.kernel.api.models.FedoraResource; 032import org.fcrepo.kernel.api.services.NodeService; 033import org.fcrepo.transform.TransformNotFoundException; 034import org.fcrepo.transform.Transformation; 035 036import org.slf4j.Logger; 037 038import javax.jcr.NamespaceRegistry; 039import javax.jcr.RepositoryException; 040import javax.jcr.Session; 041import java.io.InputStream; 042import java.io.InputStreamReader; 043import java.net.URI; 044import java.util.Collection; 045import java.util.List; 046import java.util.Map; 047import java.util.Objects; 048import java.util.function.Function; 049import java.util.stream.Collectors; 050 051import static com.hp.hpl.jena.rdf.model.ResourceFactory.createResource; 052import static org.fcrepo.kernel.api.RdfCollectors.toModel; 053import static org.slf4j.LoggerFactory.getLogger; 054 055/** 056 * Utilities for working with LDPath 057 * 058 * @author cbeer 059 */ 060public class LDPathTransform implements Transformation<List<Map<String, Collection<Object>>>> { 061 062 public static final String CONFIGURATION_FOLDER = "/fedora:system/fedora:transform/fedora:ldpath/"; 063 064 public static final String DEFAULT_TRANSFORM_RESOURCE = "fedora:Resource"; 065 066 // TODO: this mime type was made up 067 public static final String APPLICATION_RDF_LDPATH = "application/rdf+ldpath"; 068 private final InputStream query; 069 070 private static final Logger LOGGER = getLogger(LDPathTransform.class); 071 072 /** 073 * Construct a new Transform from the InputStream 074 * @param query the query 075 */ 076 public LDPathTransform(final InputStream query) { 077 this.query = query; 078 } 079 080 /** 081 * Pull a resource-type specific transform for the specified key 082 * @param resource the resource 083 * @param session the session 084 * @param nodeService a nodeService 085 * @param key the key 086 * @return resource-type specific transform 087 * @throws RepositoryException if repository exception occurred 088 */ 089 public static LDPathTransform getResourceTransform(final FedoraResource resource, final Session session, 090 final NodeService nodeService, final String key) throws RepositoryException { 091 092 final FedoraResource transformResource = nodeService.find(session, CONFIGURATION_FOLDER + key); 093 094 LOGGER.debug("Found transform resource: {}", transformResource.getPath()); 095 096 final List<URI> rdfTypes = resource.getTypes(); 097 098 LOGGER.debug("Discovered rdf types: {}", rdfTypes); 099 100 final NamespaceRegistry nsRegistry = session.getWorkspace().getNamespaceRegistry(); 101 102 // convert rdf:type with URI namespace to prefixed namespace 103 final Function<URI, String> namespaceUriToPrefix = x -> { 104 final String uriString = x.toString(); 105 try { 106 for (final String namespace : nsRegistry.getURIs()) { 107 // Ignoring zero-length namespaces return the appropriate prefix 108 if (namespace.length() > 0 && uriString.startsWith(namespace)) { 109 return uriString.replace(namespace, nsRegistry.getPrefix(namespace) + ":"); 110 } 111 } 112 return uriString; 113 } catch (final RepositoryException e) { 114 return uriString; 115 } 116 }; 117 118 final List<String> rdfStringTypes = rdfTypes.stream().map(namespaceUriToPrefix) 119 .map(stringType -> transformResource.getPath() + "/" + stringType) 120 .collect(Collectors.toList()); 121 122 final FedoraBinary transform = (FedoraBinary) transformResource.getChildren() 123 .filter(child -> rdfStringTypes.contains(child.getPath())) 124 .findFirst() 125 .orElseThrow(() -> new TransformNotFoundException( 126 String.format("Couldn't find transformation for {} and transformation key {}", 127 resource.getPath(), key))); 128 return new LDPathTransform(transform.getContent()); 129 } 130 131 @Override 132 public List<Map<String, Collection<Object>>> apply(final RdfStream stream) { 133 final LDPath<RDFNode> ldpathForResource = 134 getLdpathResource(stream); 135 136 final Resource context = createResource(stream.topic().getURI()); 137 138 try { 139 return ImmutableList.of(unsafeCast( 140 ldpathForResource.programQuery(context, new InputStreamReader(query)))); 141 } catch (final LDPathParseException e) { 142 throw new RepositoryRuntimeException(e); 143 } 144 } 145 146 @SuppressWarnings("unchecked") 147 private static <F, T> T unsafeCast(final F from) { 148 return (T) from; 149 } 150 151 @Override 152 public boolean equals(final Object other) { 153 return other instanceof LDPathTransform && ((LDPathTransform) other).query.equals(query); 154 } 155 156 @Override 157 public int hashCode() { 158 return Objects.hashCode(query); 159 } 160 161 /** 162 * Get the LDPath resource for an object 163 * @param rdfStream 164 * @return the LDPath resource for the given object 165 */ 166 private static LDPath<RDFNode> getLdpathResource(final RdfStream rdfStream) { 167 168 return new LDPath<>(new GenericJenaBackend(rdfStream.collect(toModel()))); 169 170 } 171}