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