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.rdf.impl;
019
020import static com.google.common.collect.Lists.newArrayList;
021import static org.apache.jena.rdf.model.ResourceFactory.createResource;
022
023import com.google.common.base.Converter;
024import com.google.common.collect.Lists;
025
026import org.fcrepo.kernel.api.models.FedoraResource;
027import org.fcrepo.kernel.api.exception.RepositoryRuntimeException;
028import org.fcrepo.kernel.api.identifiers.IdentifierConverter;
029
030import org.apache.jena.rdf.model.Resource;
031
032import org.fcrepo.kernel.modeshape.identifiers.HashConverter;
033import org.fcrepo.kernel.modeshape.identifiers.NamespaceConverter;
034import org.fcrepo.kernel.modeshape.identifiers.NodeResourceConverter;
035
036import javax.jcr.RepositoryException;
037import javax.jcr.Session;
038
039import java.util.List;
040
041/**
042 * A very simple {@link IdentifierConverter} which translates JCR paths into Fedora subjects with
043 * a configurable resource namespace (e.g., a baseURL).  When a REST API context is available for
044 * constructing URIs, org.fcrepo.http.commons.api.rdf.HttpResourceConverter should be used instead.
045 *
046 * @author barmintor
047 * @author ajs6f
048 * @author escowles
049 * @since 2015-04-24
050 */
051public class PrefixingIdentifierTranslator extends IdentifierConverter<Resource, FedoraResource> {
052
053
054    private static final NodeResourceConverter nodeResourceConverter = new NodeResourceConverter();
055
056    private final String resourceNamespace;
057    private final Session session;
058
059    /**
060     * Construct the graph with the provided resource namespace, which will translate JCR paths into
061     * URIs prefixed with that namespace.  Should only be used when a REST API context is not available
062     * for constructing URIs.
063     * @param session Session to lookup nodes
064     * @param resourceNamespace Resource namespace (i.e., base URL)
065    **/
066    public PrefixingIdentifierTranslator(final Session session, final String resourceNamespace) {
067        this.session = session;
068        this.resourceNamespace = resourceNamespace;
069        setTranslationChain();
070    }
071
072
073    private Converter<String, String> forward = identity();
074    private Converter<String, String> reverse = identity();
075
076    /*
077     * TODO: much of what happens with chains of translators inside these converters should be factored
078     * out into some abstract class, or post Java 8, default implementation.
079     */
080    private void setTranslationChain() {
081
082        for (final Converter<String, String> t : minimalTranslationChain) {
083            forward = forward.andThen(t);
084        }
085        for (final Converter<String, String> t : Lists.reverse(minimalTranslationChain)) {
086            reverse = reverse.andThen(t.reverse());
087        }
088    }
089
090    private static final List<Converter<String, String>> minimalTranslationChain =
091            newArrayList(new NamespaceConverter(), new HashConverter() );
092
093    @Override
094    protected FedoraResource doForward(final Resource subject) {
095        try {
096            if (!inDomain(subject)) {
097                throw new RepositoryRuntimeException("Subject " + subject + " is not in this repository");
098            }
099
100            return nodeResourceConverter.convert(session.getNode(asString(subject)));
101        } catch (final RepositoryException e) {
102            throw new RepositoryRuntimeException(e);
103        }
104    }
105
106    @Override
107    protected Resource doBackward(final FedoraResource resource) {
108        final String absPath = resource.getPath();
109
110        return toDomain(absPath);
111    }
112
113    @Override
114    public boolean inDomain(final Resource subject) {
115        return subject.isURIResource() && subject.getURI().startsWith(resourceNamespace);
116    }
117
118    @Override
119    public Resource toDomain(final String absPath) {
120        final String relativePath;
121
122        if (absPath.startsWith("/")) {
123            relativePath = absPath.substring(1);
124        } else {
125            relativePath = absPath;
126        }
127        return createResource(resourceNamespace + reverse.convert(relativePath));
128    }
129
130    @Override
131    public String asString(final Resource subject) {
132        if (!inDomain(subject)) {
133            return null;
134        }
135
136        final String path = subject.getURI().substring(resourceNamespace.length() - 1);
137
138        final String absPath = forward.convert(path);
139
140        if (absPath.isEmpty()) {
141            return "/";
142        }
143        return absPath;
144    }
145
146}