This repository has been archived by the owner on Jan 27, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add skos lookup (metafacture/metafacture-core#415)
Works like fix function 'lookup', also using a Map. The Map is build dynamically querying an RDF model.
- Loading branch information
Showing
7 changed files
with
398 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
246 changes: 246 additions & 0 deletions
246
metafix/src/main/java/org/metafacture/metafix/maps/RdfMap.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,246 @@ | ||
/* | ||
* Copyright 2013, 2014, 2021 Deutsche Nationalbibliothek et al | ||
* | ||
* Licensed under the Apache License, Version 2.0 the "License"; | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.metafacture.metafix.maps; | ||
|
||
import org.apache.jena.rdf.model.*; | ||
import org.apache.jena.riot.RDFDataMgr; | ||
import org.apache.jena.riot.RiotNotFoundException; | ||
import org.apache.jena.shared.PropertyNotFoundException; | ||
import org.metafacture.metafix.FixExecutionException; | ||
import org.metafacture.metamorph.api.Maps; | ||
import org.metafacture.metamorph.api.helpers.AbstractReadOnlyMap; | ||
|
||
import java.io.*; | ||
import java.net.MalformedURLException; | ||
import java.net.URL; | ||
import java.util.*; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
|
||
/** | ||
* Provides a dynamically build {@link Map} based on RDF files. Can be one file or a comma separated list of RDF files. | ||
* The files are supposed to be UTF-8 encoded. | ||
* <p> | ||
* | ||
* <strong>Important:</strong> All other lines that are not split in two parts | ||
* by the separator are ignored! | ||
* | ||
* @author Markus Michael Geipel | ||
* @author Pascal Christoph (dr0i) | ||
* @see org.metafacture.metamorph.maps.FileMap | ||
*/ | ||
public final class RdfMap extends AbstractReadOnlyMap<String, String> { | ||
private static final Logger LOG = LoggerFactory.getLogger(RdfMap.class); | ||
private Model model = null; | ||
private boolean isUninitialized = true; | ||
private final ArrayList<String> filenames = new ArrayList<>(); | ||
private final Map<String, String> map = new HashMap<>(); | ||
|
||
private static String targetLanguage; | ||
private static String target; | ||
private static String defaultReturn; | ||
|
||
/** | ||
* Creates an instance of {@link RdfMap}. | ||
*/ | ||
public RdfMap() { | ||
RdfMap.targetLanguage = ""; | ||
} | ||
|
||
private void init() { | ||
loadFiles(); | ||
map.put(Maps.DEFAULT_MAP_KEY, defaultReturn); | ||
String[] nsPrefixAndProperty = target.split(":"); | ||
if (nsPrefixAndProperty.length == 2) { | ||
target = model.getNsPrefixURI(nsPrefixAndProperty[0]) + nsPrefixAndProperty[1]; | ||
} | ||
|
||
isUninitialized = false; | ||
} | ||
|
||
/** | ||
* Sets a comma separated list of files which provides the {@link Model}. | ||
* | ||
* @param files a comma separated list of files | ||
*/ | ||
public void setFiles(final String files) { | ||
Collections.addAll(filenames, files.split("\\s*,\\s*")); | ||
} | ||
|
||
/** | ||
* Sets a file which provides the {@link Model}. | ||
* | ||
* @param file the file | ||
*/ | ||
public void setFile(final String file) { | ||
Collections.addAll(filenames, file); | ||
} | ||
|
||
private void loadFiles() { | ||
filenames.forEach(this::loadFile); | ||
} | ||
|
||
private void loadFile(final String file) { | ||
try { | ||
if (model == null) { | ||
model = RDFDataMgr.loadModel(file); | ||
} else { | ||
RDFDataMgr.read(model, file); | ||
} | ||
} catch (final RiotNotFoundException e) { | ||
throw new FixExecutionException("rdf file: cannot read file", e); | ||
} | ||
} | ||
|
||
private InputStream openStream(final String file) { | ||
return openAsFile(file) | ||
.orElseGet(() -> openAsResource(file) | ||
.orElseGet(() -> openAsUrl(file) | ||
.orElseThrow(() -> new FixExecutionException( | ||
"File not found: " + file)))); | ||
} | ||
|
||
private Optional<InputStream> openAsFile(final String file) { | ||
try { | ||
return Optional.of(new FileInputStream(file)); | ||
} catch (final FileNotFoundException e) { | ||
return Optional.empty(); | ||
} | ||
} | ||
|
||
private Optional<InputStream> openAsResource(final String file) { | ||
return Optional.ofNullable(Thread.currentThread() | ||
.getContextClassLoader().getResourceAsStream(file)); | ||
} | ||
|
||
private Optional<InputStream> openAsUrl(final String file) { | ||
final URL url; | ||
try { | ||
url = new URL(file); | ||
} catch (final MalformedURLException e) { | ||
return Optional.empty(); | ||
} | ||
try { | ||
return Optional.of(url.openStream()); | ||
} catch (final IOException e) { | ||
throw new UncheckedIOException(e); | ||
} | ||
} | ||
|
||
/** | ||
* Builds a Map dynamically by querying an RDF model based on key, the {@code RdfMap.target} and an optional | ||
* {@code RdfMap.targetLanguage}. | ||
* The Map acts as a cache. | ||
* <p> | ||
* To minimize the need of parameters three different querying modes are gone through. If one fails, the next is tried: | ||
* <p> | ||
* 1. get the Object based on an Subject an | ||
* | ||
* @param key the data to be looked up | ||
*/ | ||
@Override | ||
public String get(final Object key) { | ||
if (isUninitialized) { | ||
init(); | ||
} | ||
String ret = Maps.DEFAULT_MAP_KEY; | ||
if (map.containsKey(key.toString())) | ||
ret = map.get(key.toString()); | ||
else { | ||
Resource resource = ResourceFactory.createResource(key.toString()); | ||
Property targetProperty = ResourceFactory.createProperty(target); | ||
try { | ||
//first try to get LITERAL using SUBJECT and PROPERTY | ||
if (RdfMap.targetLanguage != null) { | ||
ret = model.getRequiredProperty(resource, targetProperty, RdfMap.targetLanguage).getString(); | ||
} else { | ||
ret = model.getRequiredProperty(resource, targetProperty).getString(); | ||
} | ||
} catch (PropertyNotFoundException | NullPointerException | NoSuchElementException e) { | ||
//second try to get SUBJECT using PROPERTY and LITERAL | ||
ResIterator iter = model.listSubjectsWithProperty(targetProperty); | ||
if (iter.hasNext()) { | ||
while (iter.hasNext()) { | ||
resource = iter.nextResource(); | ||
|
||
if (resource | ||
.getProperty(targetProperty) | ||
.getString().equals(key.toString())) { | ||
if (RdfMap.targetLanguage != null) { | ||
if (resource | ||
.getProperty(targetProperty).getLanguage().equals(RdfMap.targetLanguage)) { | ||
ret = resource.getURI(); | ||
} | ||
} | ||
} | ||
} | ||
//third try: get LITERAL of PREDICATE A using PREDICATE B | ||
if (ret == Maps.DEFAULT_MAP_KEY) { | ||
iter = model.listSubjectsWithProperty(targetProperty); | ||
if (iter.hasNext()) { | ||
while (iter.hasNext()) { | ||
resource = iter.nextResource(); | ||
|
||
if (resource | ||
.getProperty(targetProperty) | ||
.getString().equals(key.toString())) { | ||
Statement stmt = resource.getProperty(targetProperty); | ||
StmtIterator iterProp = | ||
resource.listProperties(targetProperty); | ||
while (iterProp.hasNext()) { | ||
stmt = iterProp.nextStatement(); | ||
if (stmt.getLanguage().equals(RdfMap.targetLanguage)) { | ||
ret = stmt.getString(); | ||
} | ||
} | ||
} | ||
|
||
} | ||
} | ||
|
||
} | ||
} else | ||
LOG.warn("Could not lookup:'" + key + " for " + target + "'. Going with default value."); | ||
} | ||
map.put(key.toString(), ret); | ||
} | ||
return ret; | ||
} | ||
|
||
|
||
@Override | ||
public Set<String> keySet() { | ||
if (isUninitialized) { | ||
init(); | ||
} | ||
return Collections.unmodifiableSet(map.keySet()); | ||
} | ||
|
||
public void setTargetLanguage(String targetLanguage) { | ||
RdfMap.targetLanguage = targetLanguage; | ||
} | ||
|
||
public void setTarget(String target) { | ||
RdfMap.target = target; | ||
} | ||
|
||
public void setDefault(String defaultReturn) { | ||
RdfMap.defaultReturn = defaultReturn; | ||
} | ||
} |
Oops, something went wrong.