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