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.impl.rdf.converters;
017
018import com.google.common.base.Converter;
019import com.google.common.collect.ImmutableBiMap;
020import com.hp.hpl.jena.rdf.model.Property;
021import com.hp.hpl.jena.rdf.model.Resource;
022
023import org.fcrepo.kernel.exception.FedoraInvalidNamespaceException;
024import org.modeshape.jcr.api.NamespaceRegistry;
025import org.modeshape.jcr.api.Namespaced;
026import org.slf4j.Logger;
027
028import javax.jcr.Node;
029import javax.jcr.RepositoryException;
030
031import java.util.Map;
032
033import static com.google.common.base.Throwables.propagate;
034import static com.hp.hpl.jena.rdf.model.ResourceFactory.createProperty;
035import static org.fcrepo.kernel.impl.rdf.JcrRdfTools.getJcrNamespaceForRDFNamespace;
036import static org.fcrepo.kernel.impl.rdf.JcrRdfTools.getRDFNamespaceForJcrNamespace;
037import static org.fcrepo.kernel.impl.utils.FedoraTypesUtils.getReferencePropertyOriginalName;
038import static org.fcrepo.kernel.impl.utils.FedoraTypesUtils.isInternalReferenceProperty;
039import static org.fcrepo.kernel.utils.NamespaceTools.getNamespaceRegistry;
040import static org.slf4j.LoggerFactory.getLogger;
041
042
043/**
044 * Convert between RDF properties and JCR properties
045 * @author cabeer
046 * @since 10/8/14
047 */
048public class PropertyConverter extends Converter<javax.jcr.Property, Property> {
049    private static final Logger LOGGER = getLogger(PropertyConverter.class);
050
051    @Override
052    protected Property doForward(final javax.jcr.Property property) {
053        LOGGER.trace("Creating predicate for property: {}",
054                property);
055        try {
056            if (property instanceof Namespaced) {
057                final Namespaced nsProperty = (Namespaced) property;
058                final String uri = nsProperty.getNamespaceURI();
059                final String localName = nsProperty.getLocalName();
060                final String rdfLocalName;
061
062                if (isInternalReferenceProperty.apply(property)) {
063                    rdfLocalName = getReferencePropertyOriginalName(localName);
064                } else {
065                    rdfLocalName = localName;
066                }
067                return createProperty(
068                        getRDFNamespaceForJcrNamespace(uri),
069                        rdfLocalName);
070            }
071            return createProperty(property.getName());
072        } catch (final RepositoryException e) {
073            throw propagate(e);
074        }
075
076    }
077
078    @Override
079    protected javax.jcr.Property doBackward(final Property property) {
080        throw new UnsupportedOperationException();
081    }
082
083    /**
084     * Given an RDF predicate value (namespace URI + local name), figure out
085     * what JCR property to use
086     *
087     * @param node the JCR node we want a property for
088     * @param predicate the predicate to map to a property name
089     * @param namespaceMapping prefix to uri namespace mapping
090     * @return the JCR property name
091     * @throws RepositoryException if repository exception occurred
092     */
093    public static String getPropertyNameFromPredicate(final Node node,
094                                                      final Resource predicate,
095                                                      final Map<String, String> namespaceMapping)
096            throws RepositoryException {
097        final NamespaceRegistry namespaceRegistry = getNamespaceRegistry.apply(node);
098        return getPropertyNameFromPredicate(namespaceRegistry,
099                predicate, namespaceMapping);
100    }
101
102    /**
103     * Get the JCR property name for an RDF predicate
104     *
105     * @param namespaceRegistry the namespace registry
106     * @param predicate the predicate to map to a property name
107     * @param namespaceMapping the namespace mapping
108     * @return JCR property name for an RDF predicate
109     * @throws RepositoryException if repository exception occurred
110     */
111    public static String getPropertyNameFromPredicate(final NamespaceRegistry namespaceRegistry,
112                                                      final Resource predicate,
113                                                      final Map<String, String> namespaceMapping)
114            throws RepositoryException {
115
116        final String rdfNamespace = predicate.getNameSpace();
117        final String rdfLocalname = predicate.getLocalName();
118
119        final String prefix;
120
121        assert (namespaceRegistry != null);
122
123        // reject if update request contains any fcr namespacess
124        if (namespaceMapping != null && namespaceMapping.containsKey("fcr")) {
125            throw new FedoraInvalidNamespaceException("Invalid fcr namespace properties " + predicate + ".");
126        }
127
128        final String namespace = getJcrNamespaceForRDFNamespace(rdfNamespace);
129
130        if (namespaceRegistry.isRegisteredUri(namespace)) {
131            LOGGER.debug("Discovered namespace: {} in namespace registry.",namespace);
132            prefix = namespaceRegistry.getPrefix(namespace);
133        } else {
134            LOGGER.debug("Didn't discover namespace: {} in namespace registry.",namespace);
135            final ImmutableBiMap<String, String> nsMap =
136                    ImmutableBiMap.copyOf(namespaceMapping);
137            if (nsMap.containsValue(namespace)) {
138                LOGGER.debug("Discovered namespace: {} in namespace map: {}.", namespace,
139                        nsMap);
140                prefix = nsMap.inverse().get(namespace);
141                namespaceRegistry.registerNamespace(prefix, namespace);
142            } else {
143                prefix = namespaceRegistry.registerNamespace(namespace);
144            }
145        }
146
147        final String propertyName = prefix + ":" + rdfLocalname;
148
149        LOGGER.debug("Took RDF predicate {} and translated it to JCR property {}", namespace, propertyName);
150
151        return propertyName;
152
153    }
154
155}