View Javadoc

1   /*
2    * Copyright 2015 Data Archiving and Networked Services (an institute of
3    * Koninklijke Nederlandse Akademie van Wetenschappen), King's College London,
4    * Georg-August-Universitaet Goettingen Stiftung Oeffentlichen Rechts
5    *
6    * Licensed under the EUPL, Version 1.1 or – as soon they will be approved by
7    * the European Commission - subsequent versions of the EUPL (the "Licence");
8    * You may not use this work except in compliance with the Licence.
9    * You may obtain a copy of the Licence at:
10   *
11   * https://joinup.ec.europa.eu/software/page/eupl
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the Licence is distributed on an "AS IS" basis,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the Licence for the specific language governing
17   * permissions and limitations under the Licence.
18   */
19  
20  package eu.ehri.project.exporters.cvoc;
21  
22  import com.tinkerpop.blueprints.Vertex;
23  import com.tinkerpop.frames.FramedGraph;
24  import eu.ehri.project.definitions.Ontology;
25  import eu.ehri.project.importers.cvoc.SkosRDFVocabulary;
26  import eu.ehri.project.models.UnknownProperty;
27  import eu.ehri.project.models.base.Description;
28  import eu.ehri.project.models.cvoc.Concept;
29  import eu.ehri.project.models.cvoc.Vocabulary;
30  import eu.ehri.project.utils.LanguageHelpers;
31  import org.apache.jena.rdf.model.Model;
32  import org.apache.jena.rdf.model.ModelFactory;
33  import org.apache.jena.rdf.model.Property;
34  import org.apache.jena.rdf.model.Resource;
35  import org.apache.jena.vocabulary.RDF;
36  import org.slf4j.Logger;
37  import org.slf4j.LoggerFactory;
38  
39  import java.io.IOException;
40  import java.io.OutputStream;
41  import java.net.URI;
42  import java.util.List;
43  import java.util.Optional;
44  
45  /**
46   * Export SKOS RDF.
47   */
48  public class JenaSkosExporter implements SkosExporter {
49  
50      private static final Logger logger = LoggerFactory.getLogger(JenaSkosExporter.class);
51      public final static String DC_URI = "http://purl.org/dc/elements/1.1/";
52  
53      private final FramedGraph<?> framedGraph;
54      private final Vocabulary vocabulary;
55      private final String format;
56  
57      public JenaSkosExporter(final FramedGraph<?> framedGraph, final Vocabulary vocabulary,
58              String format) {
59          this.framedGraph = framedGraph;
60          this.vocabulary = vocabulary;
61          this.format = format;
62      }
63  
64      public JenaSkosExporter(final FramedGraph<?> framedGraph, final Vocabulary vocabulary) {
65          this(framedGraph, vocabulary, null);
66      }
67  
68      public JenaSkosExporter setFormat(String format) {
69          return new JenaSkosExporter(
70                  framedGraph, vocabulary, format);
71      }
72  
73      @Override
74      public void export(OutputStream outputStream, String base) throws IOException {
75          Model model = export(base);
76          model.getWriter(format).write(model, outputStream, null);
77      }
78  
79      private String getUri(Concept concept, String baseUri) {
80          String fallback = baseUri + concept.getIdentifier();
81          String orig = concept.getProperty(Ontology.URI_KEY);
82          return Optional.ofNullable(orig).orElse(fallback);
83      }
84  
85      public Model export(String base) throws IOException {
86  
87          String baseUri = base == null ? SkosRDFVocabulary.DEFAULT_BASE_URI : base;
88          Iterable<Concept> concepts = vocabulary.getConcepts();
89          Model model = ModelFactory.createDefaultModel();
90          Resource skosConcept = model.createResource(SkosRDFVocabulary.CONCEPT.getURI().toString());
91          model.setNsPrefixes(SkosRDFVocabulary.NAMESPACES);
92          Property prefLabelProp = model
93                  .createProperty(SkosRDFVocabulary.PREF_LABEL.getURI().toString());
94          Property inSchemeProp = model
95                  .createProperty(SkosRDFVocabulary.IN_SCHEME.getURI().toString());
96          Property topConceptProp = model
97                  .createProperty(SkosRDFVocabulary.HAS_TOP_CONCEPT.getURI().toString());
98  
99          Resource vocabResource = model.createResource(baseUri + vocabulary.getId());
100         model.add(vocabResource, RDF.type, model
101                 .createResource(SkosRDFVocabulary.CONCEPT_SCHEME.getURI().toString()));
102 
103         // Write name and description as DC elements.
104         for (String dcElement : new String[]{"name", "description"}) {
105             Object prop = vocabulary.getProperty(dcElement);
106             if (prop != null) {
107                 writeListOrScalar(model, vocabResource,
108                         model.createProperty(DC_URI + dcElement), prop, "en");
109             }
110         }
111 
112         for (Concept concept : concepts) {
113             Vertex cv = concept.asVertex();
114             Resource resource = model.createResource(getUri(concept, baseUri));
115             model.add(resource, RDF.type, skosConcept);
116             model.add(resource, inSchemeProp, vocabResource);
117 
118             for (String key : cv.getPropertyKeys()) {
119                 writeProperty(model, resource, key, cv.getProperty(key), null);
120             }
121 
122             for (Description description : concept.getDescriptions()) {
123                 Vertex cdv = description.asVertex();
124                 String lang = LanguageHelpers.iso639DashOneCode(description.getLanguageOfDescription());
125                 model.add(resource, prefLabelProp, description.getName(), lang);
126                 for (String key : cdv.getPropertyKeys()) {
127                     writeProperty(model, resource, key, cdv.getProperty(key), lang);
128                 }
129 
130                 // In some cases there'll be an unknown property with a key
131                 // such as owl:sameAs and a value pointing to some other URL.
132                 for (UnknownProperty prop : description.getUnknownProperties()) {
133                     for (String key : prop.getPropertyKeys()) {
134                         String value = prop.getProperty(key);
135                         if (SkosRDFVocabulary.RELATION_PROPS.containsKey(key)) {
136                             model.add(resource,
137                                     model.createProperty(SkosRDFVocabulary.RELATION_PROPS.get(key).toString()), value);
138                         }
139                     }
140                 }
141             }
142 
143             // if there are no broader concepts, assume it's a top concept
144             if (!concept.getBroaderConcepts().iterator().hasNext()) {
145                 model.add(vocabResource, topConceptProp, resource);
146             }
147 
148             for (Concept other : concept.getBroaderConcepts()) {
149                 Resource otherResource = model.createResource(getUri(other, baseUri));
150                 model.add(resource, model.createProperty(SkosRDFVocabulary.BROADER.getURI().toString()), otherResource);
151             }
152             for (Concept other : concept.getNarrowerConcepts()) {
153                 Resource otherResource = model.createResource(getUri(other, baseUri));
154                 model.add(resource, model.createProperty(SkosRDFVocabulary.NARROWER.getURI().toString()), otherResource);
155             }
156             for (Concept other : concept.getRelatedConcepts()) {
157                 Resource otherResource = model.createResource(getUri(other, baseUri));
158                 model.add(resource, model.createProperty(SkosRDFVocabulary.RELATED.getURI().toString()), otherResource);
159             }
160         }
161         return model;
162     }
163 
164     private void writeProperty(Model model, Resource resource, String key, Object property, String lang) {
165         if (SkosRDFVocabulary.LANGUAGE_PROPS.containsKey(key)) {
166             // We map certain URIs to the same internal property, e.g. scopeNote/comment.
167             // Ignore all but the first URI when exporting since they're assumed to
168             // be in order of preference.
169             for (URI uri : SkosRDFVocabulary.LANGUAGE_PROPS.get(key).subList(0, 1)) {
170                 writeListOrScalar(model, resource, model.createProperty(uri.toString()), property, lang);
171             }
172         } else if (SkosRDFVocabulary.GENERAL_PROPS.containsKey(key)) {
173             writeListOrScalar(model, resource,
174                     model.createProperty(SkosRDFVocabulary.GENERAL_PROPS.get(key).toString()), property, null);
175         }
176     }
177 
178     private void writeListOrScalar(Model model, Resource resource,
179             Property property, Object listOrScalar, String lang) {
180         if (listOrScalar instanceof List) {
181             List<?> list = (List) listOrScalar;
182             for (Object obj : list) {
183                 logger.trace("Writing list property: {} -> {}", property, obj);
184                 writeObject(model, resource, property, obj, lang);
185             }
186         } else {
187             logger.trace("Writing scalar property: {} -> {}", property, listOrScalar);
188             writeObject(model, resource, property, listOrScalar, lang);
189         }
190     }
191 
192     private void writeObject(Model model, Resource resource,
193             Property property, Object value, String lang) {
194         if (value instanceof String) {
195             model.add(resource, property, (String) value, lang);
196         } else {
197             model.add(resource, property, model.createTypedLiteral(value));
198         }
199     }
200 }