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