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.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.Collections.emptyIterator;
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.RdfLexicon.REPOSITORY_NAMESPACE;
042import static org.slf4j.LoggerFactory.getLogger;
043
044import java.util.Iterator;
045import java.util.Map;
046
047import javax.jcr.RepositoryException;
048import javax.jcr.nodetype.PropertyDefinition;
049
050import org.fcrepo.kernel.utils.iterators.RdfStream;
051import org.slf4j.Logger;
052
053import com.google.common.collect.ImmutableMap;
054import com.hp.hpl.jena.datatypes.xsd.XSDDatatype;
055import com.hp.hpl.jena.graph.Node;
056import com.hp.hpl.jena.graph.Triple;
057
058/**
059 * Utility for moving Property Definitions into RDFS triples
060 * @author cbeer
061 */
062public class PropertyDefinitionToTriples extends ItemDefinitionToTriples<PropertyDefinition> {
063
064    private static final Logger LOGGER = getLogger(PropertyDefinitionToTriples.class);
065
066    /**
067     * A JCR type for which we know no RDF equivalent.
068     */
069    private static final Node UNMAPPED_TYPE = createURI(REPOSITORY_NAMESPACE
070            + "UnmappedType");
071
072    /**
073     * A map from JCR types to RDF types.
074     */
075    private static final Map<Integer, XSDDatatype> JCR_TYPE_TO_XSD_DATATYPE =
076            ImmutableMap.<Integer, XSDDatatype>builder()
077            .put(BOOLEAN, XSDboolean)
078            .put(DATE, XSDdate)
079            .put(DECIMAL, XSDdecimal)
080            .put(DOUBLE, XSDdecimal)
081            .put(LONG, XSDlong)
082            .put(URI, XSDanyURI)
083            .put(REFERENCE, XSDanyURI)
084            .put(WEAKREFERENCE, XSDanyURI)
085            .put(PATH, XSDanyURI)
086            .put(BINARY, XSDstring)
087            .put(STRING, XSDstring).build();
088
089    /**
090     * Translate ItemDefinitions into triples. The definitions will hang off
091     * the provided RDF Node
092     * @param domain the given domain
093     */
094    public PropertyDefinitionToTriples(final Node domain) {
095        super(domain);
096    }
097
098    @Override
099    public Iterator<Triple> apply(final PropertyDefinition input) {
100
101        if (!input.getName().contains(":")) {
102            LOGGER.debug("Received property definition with no namespace: {}",
103                    input.getName());
104            LOGGER.debug("This cannot be serialized into several RDF formats, " +
105                                 "so we assume it is internal and discard it.");
106            // TODO find a better way...
107            return emptyIterator();
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 new RdfStream(propertyTriple).concat(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}