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        // reject if update request contains any fcr namespaces
117        if (namespaceMapping != null && namespaceMapping.containsKey("fcr")) {
118            throw new FedoraInvalidNamespaceException("Invalid fcr namespace properties " + predicate + ".");
119        }
120
121        final String rdfNamespace = predicate.getNameSpace();
122
123        // log warning if the user-supplied namespace doesn't match value from predicate.getNameSpace(),
124        // e.g., if the Jena method returns "http://" for "http://myurl.org" (no terminating character).
125        if (namespaceMapping != null && !namespaceMapping.containsValue(rdfNamespace)) {
126            LOGGER.warn("The namespace of predicate: {} was possibly misinterpreted as: {}."
127                    , predicate, rdfNamespace);
128        }
129
130        final String rdfLocalname = predicate.getLocalName();
131
132        final String prefix;
133
134        assert (namespaceRegistry != null);
135
136        final String namespace = getJcrNamespaceForRDFNamespace(rdfNamespace);
137
138        if (namespaceRegistry.isRegisteredUri(namespace)) {
139            LOGGER.debug("Discovered namespace: {} in namespace registry.",namespace);
140            prefix = namespaceRegistry.getPrefix(namespace);
141        } else {
142            LOGGER.debug("Didn't discover namespace: {} in namespace registry.",namespace);
143            final ImmutableBiMap<String, String> nsMap =
144                    ImmutableBiMap.copyOf(namespaceMapping);
145            if (nsMap.containsValue(namespace)) {
146                LOGGER.debug("Discovered namespace: {} in namespace map: {}.", namespace,
147                        nsMap);
148                prefix = nsMap.inverse().get(namespace);
149                namespaceRegistry.registerNamespace(prefix, namespace);
150            } else {
151                prefix = namespaceRegistry.registerNamespace(namespace);
152            }
153        }
154
155        final String propertyName = prefix + ":" + rdfLocalname;
156
157        LOGGER.debug("Took RDF predicate {} and translated it to JCR property {}", namespace, propertyName);
158
159        return propertyName;
160
161    }
162
163}