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;
019
020import static org.fcrepo.kernel.api.utils.SubjectMappingUtil.mapSubject;
021import static java.util.stream.Stream.empty;
022import static org.fcrepo.kernel.api.RequiredRdfContext.MINIMAL;
023import static org.fcrepo.kernel.modeshape.utils.FedoraTypesUtils.isNonRdfSourceDescription;
024import static org.fcrepo.kernel.api.RdfLexicon.LDPCV_TIME_MAP;
025import static org.slf4j.LoggerFactory.getLogger;
026
027import javax.jcr.Node;
028import javax.jcr.PathNotFoundException;
029import javax.jcr.RepositoryException;
030
031import org.apache.jena.graph.Triple;
032import org.apache.jena.rdf.model.Resource;
033import org.fcrepo.kernel.api.RdfStream;
034import org.fcrepo.kernel.api.TripleCategory;
035import org.fcrepo.kernel.api.exception.PathNotFoundRuntimeException;
036import org.fcrepo.kernel.api.exception.RepositoryRuntimeException;
037import org.fcrepo.kernel.api.identifiers.IdentifierConverter;
038import org.fcrepo.kernel.api.models.FedoraResource;
039import org.fcrepo.kernel.api.models.NonRdfSourceDescription;
040import org.fcrepo.kernel.api.rdf.DefaultRdfStream;
041import org.fcrepo.kernel.modeshape.rdf.impl.InternalIdentifierTranslator;
042import org.slf4j.Logger;
043
044import java.util.Calendar;
045import java.util.Set;
046import java.util.stream.Stream;
047
048/**
049 * Abstraction for a Fedora datastream backed by a JCR node.
050 *
051 * @author ajs6f
052 * @since Feb 21, 2013
053 */
054public class NonRdfSourceDescriptionImpl extends FedoraResourceImpl implements NonRdfSourceDescription {
055
056    private static final Logger LOGGER = getLogger(NonRdfSourceDescriptionImpl.class);
057
058    /**
059     * The JCR node for this datastream
060     *
061     * @param n an existing {@link Node}
062     */
063    public NonRdfSourceDescriptionImpl(final Node n) {
064        super(n);
065    }
066
067    @Override
068    public FedoraResource getDescribedResource() {
069        return new FedoraBinaryImpl(getContentNode());
070    }
071
072    private Node getContentNode() {
073        LOGGER.trace("Retrieved datastream content node.");
074        try {
075            if (isMemento()) {
076                final String mementoName = node.getName();
077                return node.getParent().getParent().getParent().getNode(LDPCV_TIME_MAP).getNode(mementoName);
078            }
079            return node.getParent();
080        } catch (final PathNotFoundException e) {
081            throw new PathNotFoundRuntimeException(e);
082        } catch (final RepositoryException e) {
083            throw new RepositoryRuntimeException(e);
084        }
085    }
086
087    @Override
088    public RdfStream getTriples(final IdentifierConverter<Resource, FedoraResource> idTranslator,
089                                final Set<? extends TripleCategory> contexts) {
090        final FedoraResource described = getOriginalResource().getDescribedResource();
091
092        final org.apache.jena.graph.Node describedNode = idTranslator.reverse().convert(described).asNode();
093        final String resourceUri = idTranslator.reverse().convert(this).getURI();
094
095        Stream<Triple> triples = contexts.stream()
096                .filter(contextMap::containsKey)
097                .map(x -> contextMap.get(x).apply(this).apply(idTranslator).apply(contexts.contains(MINIMAL)))
098                .reduce(empty(), Stream::concat)
099                .map(t -> mapSubject(t, resourceUri, describedNode));
100
101        // if a memento, convert subjects to original resource and object references from referential integrity
102        // ignoring internal URL back the original external URL.
103        if (isMemento()) {
104            final IdentifierConverter<Resource, FedoraResource> internalIdTranslator =
105                    new InternalIdentifierTranslator(getSession());
106            triples = triples.map(convertMementoReferences(idTranslator, internalIdTranslator));
107        }
108
109        return new DefaultRdfStream(idTranslator.reverse().convert(described).asNode(), triples);
110    }
111
112    /**
113     * Check if the node has a fedora:datastream mixin
114     *
115     * @param node node to check
116     * @return whether the node has a fedora:datastream mixin
117     */
118    public static boolean hasMixin(final Node node) {
119        return isNonRdfSourceDescription.test(node);
120    }
121
122    /**
123     * Overrides the superclass to propagate updates to certain properties to the binary if explicitly set.
124     */
125    @Override
126    public void touch(final boolean includeMembershipResource, final Calendar createdDate, final String createdUser,
127                      final Calendar modifiedDate, final String modifyingUser) throws RepositoryException {
128        super.touch(includeMembershipResource, createdDate, createdUser, modifiedDate, modifyingUser);
129        if (createdDate != null || createdUser != null || modifiedDate != null || modifyingUser != null) {
130            ((FedoraBinaryImpl) getDescribedResource()).touch(false, createdDate, createdUser,
131                    modifiedDate, modifyingUser);
132        }
133    }
134
135}