![JAR search and dependency download from the Maven repository](/logo.png)
it.unibz.inf.ontop.spec.mapping.parser.impl.OntopNativeMappingParser Maven / Gradle / Ivy
The newest version!
package it.unibz.inf.ontop.spec.mapping.parser.impl;
/*
* #%L
* ontop-obdalib-core
* %%
* Copyright (C) 2009 - 2014 Free University of Bozen-Bolzano
* %%
* 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.
* #L%
*/
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.inject.Inject;
import it.unibz.inf.ontop.exception.*;
import it.unibz.inf.ontop.injection.TargetQueryParserFactory;
import it.unibz.inf.ontop.spec.mapping.TargetAtom;
import it.unibz.inf.ontop.injection.SQLPPMappingFactory;
import it.unibz.inf.ontop.injection.SpecificationFactory;
import it.unibz.inf.ontop.spec.mapping.PrefixManager;
import it.unibz.inf.ontop.spec.mapping.SQLPPSourceQueryFactory;
import it.unibz.inf.ontop.spec.mapping.parser.SQLMappingParser;
import it.unibz.inf.ontop.spec.mapping.parser.TargetQueryParser;
import it.unibz.inf.ontop.spec.mapping.pp.SQLPPMapping;
import it.unibz.inf.ontop.spec.mapping.pp.SQLPPTriplesMap;
import it.unibz.inf.ontop.spec.mapping.pp.impl.OntopNativeSQLPPTriplesMap;
import org.apache.commons.rdf.api.Graph;
import java.io.*;
import java.util.*;
import java.util.function.Supplier;
import static it.unibz.inf.ontop.spec.mapping.OntopNativeMappingSyntax.*;
/**
* Mapping parser specific to the Ontop Native Mapping Language for SQL.
*
* Available through Guice.
*
*/
public class OntopNativeMappingParser implements SQLMappingParser {
private static final ImmutableList DEPRECATED_TAGS = ImmutableList.of(
"[ClassDeclaration]", "[ObjectPropertyDeclaration]", "[DataPropertyDeclaration]");
private static final String SOURCE_DECLARATION_TAG = "[SourceDeclaration]";
private static final String COMMENT_SYMBOL = ";";
private final TargetQueryParserFactory targetQueryParserFactory;
private final SQLPPMappingFactory ppMappingFactory;
private final SpecificationFactory specificationFactory;
private final SQLPPSourceQueryFactory sourceQueryFactory;
/**
* Create an SQL Mapping Parser for generating an OBDA model.
*/
@Inject
private OntopNativeMappingParser(SpecificationFactory specificationFactory,
TargetQueryParserFactory targetQueryParserFactory,
SQLPPMappingFactory ppMappingFactory,
SQLPPSourceQueryFactory sourceQueryFactory) {
this.targetQueryParserFactory = targetQueryParserFactory;
this.ppMappingFactory = ppMappingFactory;
this.specificationFactory = specificationFactory;
this.sourceQueryFactory = sourceQueryFactory;
}
/**
* Parsing is not done at construction time because our dependency
* injection framework (Guice) does not manage exceptions nicely.
*/
@Override
public SQLPPMapping parse(File file) throws InvalidMappingException, MappingIOException {
if (!file.exists()) {
throw new MappingIOException("WARNING: Cannot locate OBDA file at: " + file.getPath());
}
if (!file.canRead()) {
throw new MappingIOException(String.format("Error while reading the file located at %s.\n" +
"Make sure you have the read permission at the location specified.", file.getAbsolutePath()));
}
try (Reader reader = new FileReader(file)) {
return load(reader, specificationFactory, ppMappingFactory, file.getName());
}
catch (IOException e) {
throw new MappingIOException(e);
}
}
@Override
public SQLPPMapping parse(Reader reader) throws InvalidMappingException, MappingIOException {
return load(reader, specificationFactory, ppMappingFactory, ".obda file");
}
@Override
public SQLPPMapping parse(Graph mappingGraph) {
throw new IllegalArgumentException("The Ontop native mapping language has no RDF serialization. Passing a RDF graph" +
"to the OntopNativeMappingParser is thus invalid.");
}
private SQLPPMapping load(Reader reader, SpecificationFactory specificationFactory,
SQLPPMappingFactory ppMappingFactory, String fileName)
throws MappingIOException, InvalidMappingException {
ImmutableMap.Builder prefixes = ImmutableMap.builder();
ImmutableList.Builder mappings = ImmutableList.builder();
List invalidMappingIndicators = new ArrayList<>();
try (LineNumberReader lineNumberReader = new LineNumberReader(reader)) {
try {
String line;
while ((line = lineNumberReader.readLine()) != null) {
//noinspection StatementWithEmptyBody
if (line.isEmpty() || line.trim().indexOf(COMMENT_SYMBOL) == 0) {
// skip blank lines and comment lines (starting with ;)
}
else if (line.contains(PREFIX_DECLARATION_TAG)) {
prefixes.putAll(readPrefixDeclaration(lineNumberReader));
}
else if (line.contains(MAPPING_DECLARATION_TAG)) {
TargetQueryParser parser = targetQueryParserFactory.createParser(specificationFactory.createPrefixManager(prefixes.build()));
mappings.addAll(readMappingDeclaration(lineNumberReader, parser, invalidMappingIndicators));
}
// These exceptions will be rethrown as IOException and then as MappingIOException
else {
DEPRECATED_TAGS.stream()
.filter(line::contains)
.forEach(tag -> { throw new RuntimeException("The tag " + tag
+ " is no longer supported. You may safely remove the content from the file."); });
if (line.contains(SOURCE_DECLARATION_TAG)) {
throw new RuntimeException("Source declaration is not supported anymore (since 3.0). " +
"Please give this information with the Ontop configuration.");
}
throw new RuntimeException("Unknown syntax: " + line);
}
}
}
catch (Exception e) {
throw new IOException(String.format("ERROR reading %s at line: %d\nMESSAGE: %s", fileName,
lineNumberReader.getLineNumber(), e.getMessage()), e);
}
}
catch (IOException e) {
throw new MappingIOException(e);
}
if (!invalidMappingIndicators.isEmpty())
throw new InvalidMappingException("\nThe syntax of the mapping is invalid (and therefore cannot be processed). Problems:\n\n"
+ String.join("\n", invalidMappingIndicators));
PrefixManager prefixManager = specificationFactory.createPrefixManager(prefixes.build());
return ppMappingFactory.createSQLPreProcessedMapping(mappings.build(), prefixManager);
}
/*
* Helper methods related to load file.
*/
private static ImmutableMap readPrefixDeclaration(BufferedReader reader) throws IOException {
ImmutableMap.Builder prefixes = ImmutableMap.builder();
String line;
while (!(line = reader.readLine()).isEmpty()) {
String[] tokens = line.split("[\t| ]+");
prefixes.put(tokens[0], tokens[1]);
}
return prefixes.build();
}
private final class MappingBuilder {
private final List invalidMappingIndicators;
private final Supplier line;
private String mappingId = "";
private final ImmutableList.Builder sourceQuery = ImmutableList.builder();
private String targetString = null;
private ImmutableList targetQuery = null;
boolean isMappingValid = true;
MappingBuilder(List invalidMappingIndicators, Supplier line) {
this.invalidMappingIndicators = invalidMappingIndicators;
this.line = line;
}
private void fail(String message) {
invalidMappingIndicators.add((!mappingId.isEmpty() ? String.format("MappingId = '%s'\n", mappingId) : "") + message);
isMappingValid = false;
}
boolean isValid() { return isMappingValid; }
Optional build() {
return (!mappingId.isEmpty() && isMappingValid)
? Optional.of(new OntopNativeSQLPPTriplesMap(
mappingId,
sourceQueryFactory.createSourceQuery(
String.join("\n", sourceQuery.build())),
targetString,
targetQuery))
: Optional.empty();
}
void setMappingId(String value) {
mappingId = value;
if (mappingId.isEmpty())
fail(String.format("Line %d: Mapping ID is missing\n", line.get()));
}
void setTarget(String value, TargetQueryParser parser) {
targetString = value;
if (!targetString.isEmpty()) {
try {
targetQuery = parser.parse(targetString);
}
catch (TargetQueryParserException e) {
fail(String.format("Line %d: Invalid target: '%s'\nDebug information\n%s", line.get(), targetString, e.getMessage()));
}
}
else
fail(String.format("Line %d: Target is missing\n", line.get()));
}
void setSource(String value) {
if (!value.isEmpty())
sourceQuery.add(value);
else
fail(String.format("Line %d: Source query is missing\n", line.get()));
}
}
/**
* @param reader LineNumberReader
* @param parser TargetQueryParser
* @param invalidMappingIndicators Read-write list of error indicators.
* @return SQLPPTriplesMaps of the current source
* @throws IOException
*/
private ImmutableList readMappingDeclaration(LineNumberReader reader,
TargetQueryParser parser,
List invalidMappingIndicators) throws IOException {
ImmutableList.Builder mappings = ImmutableList.builder();
MappingBuilder current = new MappingBuilder(invalidMappingIndicators, reader::getLineNumber);
String currentLabel = ""; // the reader is working on which label
String line;
while ((line = reader.readLine())!= null) {
if (line.trim().equals(END_COLLECTION_SYMBOL)) {
current.build().ifPresent(mappings::add);
return mappings.build();
}
else if (line.isEmpty()) {
current.build().ifPresent(mappings::add);
current = new MappingBuilder(invalidMappingIndicators, reader::getLineNumber);
}
// skip the comment lines (which have ; as their first non-space symbol)
// and invalid mappings
else if (line.trim().indexOf(COMMENT_SYMBOL) != 0 && current.isValid()) {
String[] tokens = line.split("[\t| ]+", 2);
String value = tokens[tokens.length - 1].trim();
if (tokens.length > 1) {
String label = tokens[0].trim();
if (!label.isEmpty())
currentLabel = label;
}
switch (currentLabel) {
case MAPPING_ID_LABEL:
current.setMappingId(value);
break;
case TARGET_LABEL:
current.setTarget(value, parser);
break;
case SOURCE_LABEL:
current.setSource(value);
break;
default:
throw new IOException(String.format("Unknown parameter name \"%s\" at line: %d.", tokens[0], reader.getLineNumber()));
}
}
}
throw new IOException(String.format("End collection symbol %s is missing.", END_COLLECTION_SYMBOL));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy