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.modeshape.rdf.impl.mappings;
017
018import static com.google.common.base.Throwables.propagate;
019import static com.hp.hpl.jena.datatypes.xsd.XSDDatatype.XSDanyURI;
020import static com.hp.hpl.jena.datatypes.xsd.XSDDatatype.XSDboolean;
021import static com.hp.hpl.jena.datatypes.xsd.XSDDatatype.XSDdate;
022import static com.hp.hpl.jena.datatypes.xsd.XSDDatatype.XSDdecimal;
023import static com.hp.hpl.jena.datatypes.xsd.XSDDatatype.XSDlong;
024import static com.hp.hpl.jena.datatypes.xsd.XSDDatatype.XSDstring;
025import static com.hp.hpl.jena.graph.NodeFactory.createURI;
026import static com.hp.hpl.jena.graph.Triple.create;
027import static com.hp.hpl.jena.vocabulary.RDFS.range;
028import static java.util.stream.Stream.of;
029import static javax.jcr.PropertyType.BINARY;
030import static javax.jcr.PropertyType.BOOLEAN;
031import static javax.jcr.PropertyType.DATE;
032import static javax.jcr.PropertyType.DECIMAL;
033import static javax.jcr.PropertyType.DOUBLE;
034import static javax.jcr.PropertyType.LONG;
035import static javax.jcr.PropertyType.PATH;
036import static javax.jcr.PropertyType.REFERENCE;
037import static javax.jcr.PropertyType.STRING;
038import static javax.jcr.PropertyType.URI;
039import static javax.jcr.PropertyType.WEAKREFERENCE;
040import static javax.jcr.PropertyType.nameFromValue;
041import static org.fcrepo.kernel.api.RdfLexicon.REPOSITORY_NAMESPACE;
042import static org.slf4j.LoggerFactory.getLogger;
043
044import java.util.stream.Stream;
045import java.util.Map;
046
047import javax.jcr.RepositoryException;
048import javax.jcr.nodetype.PropertyDefinition;
049
050import org.slf4j.Logger;
051
052import com.google.common.collect.ImmutableMap;
053import com.hp.hpl.jena.datatypes.xsd.XSDDatatype;
054import com.hp.hpl.jena.graph.Node;
055import com.hp.hpl.jena.graph.Triple;
056
057/**
058 * Utility for moving Property Definitions into RDFS triples
059 * @author cbeer
060 */
061public class PropertyDefinitionToTriples extends ItemDefinitionToTriples<PropertyDefinition> {
062
063    private static final Logger LOGGER = getLogger(PropertyDefinitionToTriples.class);
064
065    /**
066     * A JCR type for which we know no RDF equivalent.
067     */
068    private static final Node UNMAPPED_TYPE = createURI(REPOSITORY_NAMESPACE
069            + "UnmappedType");
070
071    /**
072     * A map from JCR types to RDF types.
073     */
074    private static final Map<Integer, XSDDatatype> JCR_TYPE_TO_XSD_DATATYPE =
075            ImmutableMap.<Integer, XSDDatatype>builder()
076            .put(BOOLEAN, XSDboolean)
077            .put(DATE, XSDdate)
078            .put(DECIMAL, XSDdecimal)
079            .put(DOUBLE, XSDdecimal)
080            .put(LONG, XSDlong)
081            .put(URI, XSDanyURI)
082            .put(REFERENCE, XSDanyURI)
083            .put(WEAKREFERENCE, XSDanyURI)
084            .put(PATH, XSDanyURI)
085            .put(BINARY, XSDstring)
086            .put(STRING, XSDstring).build();
087
088    /**
089     * Translate ItemDefinitions into triples. The definitions will hang off
090     * the provided RDF Node
091     * @param domain the given domain
092     */
093    public PropertyDefinitionToTriples(final Node domain) {
094        super(domain);
095    }
096
097    @Override
098    public Stream<Triple> apply(final PropertyDefinition input) {
099
100        if (!input.getName().contains(":")) {
101            LOGGER.debug("Received property definition with no namespace: {}",
102                    input.getName());
103            LOGGER.debug("This cannot be serialized into several RDF formats, " +
104                                 "so we assume it is internal and discard it.");
105            return Stream.empty();
106        }
107
108        try {
109            // skip range declaration for unknown types
110            final int requiredType = input.getRequiredType();
111
112            final Node rangeForJcrType = getRangeForJcrType(requiredType);
113
114            if (!rangeForJcrType.equals(UNMAPPED_TYPE)) {
115                LOGGER.trace("Adding RDFS:range for property: {} with required type: {} as: {}",
116                    input.getName(), nameFromValue(requiredType), rangeForJcrType.getURI());
117                final Triple propertyTriple =
118                    create(getResource(input).asNode(), range.asNode(),
119                            rangeForJcrType);
120                return Stream.concat(of(propertyTriple), super.apply(input));
121            }
122            LOGGER.trace(
123                    "Skipping RDFS:range for property: {} with unmappable type: {}",
124                    input.getName(), nameFromValue(requiredType));
125            return super.apply(input);
126        } catch (final RepositoryException e) {
127            throw propagate(e);
128        }
129    }
130
131    /**
132     * Convert a JCR type to an RDF data type.
133     *
134     * @param requiredType a JCR PropertyType
135     * @return rdf node of data type
136     */
137    private static Node getRangeForJcrType(final int requiredType) {
138        return JCR_TYPE_TO_XSD_DATATYPE.containsKey(requiredType)
139            ? createURI(JCR_TYPE_TO_XSD_DATATYPE.get(requiredType).getURI())
140            : UNMAPPED_TYPE;
141    }
142}