/*
* Copyright (c) 2013-2015 Josef Hardi <josef.hardi@gmail.com>
*
* 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 com.obidea.semantika.knowledgebase;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.obidea.semantika.expression.base.IAtom;
import com.obidea.semantika.expression.base.IConstant;
import com.obidea.semantika.expression.base.IExpressionObject;
import com.obidea.semantika.expression.base.IFunction;
import com.obidea.semantika.expression.base.ISignature;
import com.obidea.semantika.expression.base.ITerm;
import com.obidea.semantika.expression.base.IUriReference;
import com.obidea.semantika.expression.base.IVariable;
import com.obidea.semantika.expression.base.TermUtils;
import com.obidea.semantika.mapping.IUriTemplate;
import com.obidea.semantika.mapping.MappingObjectFactory;
import com.obidea.semantika.util.Serializer;
public class Unifier
{
public static TermSubstitutionBinding findSubstitution(IAtom a1, IAtom a2) throws UnificationException
{
TermSubstitutionBinding theta = TermSubstitutionBinding.createEmptyBinding();
findSubstitution(a1, a2, theta);
return theta;
}
public static TermSubstitutionBinding findSubstitution(ITerm t1, ITerm t2) throws UnificationException
{
TermSubstitutionBinding theta = TermSubstitutionBinding.createEmptyBinding();
findSubstitution(t1, t2, theta);
return theta;
}
public static void findSubstitution(IAtom a1, IAtom a2, TermSubstitutionBinding substitution) throws UnificationException
{
/*
* The fastest way to ignore finding the unifier is to compare both atoms syntactically. This
* checking will throw UnificationException if the two atoms are not similar.
*/
checkSyntacticalUnification(a1, a2);
IAtom ac1 = copy(a1);
IAtom ac2 = copy(a2);
for (int i = 0; i < a1.getArity(); i++) {
final ITerm t1 = ac1.getTerm(i);
final ITerm t2 = ac2.getTerm(i);
findSubstitution(t1, t2, substitution);
ac1.apply(substitution);
ac2.apply(substitution);
}
checkOverflowUnification(ac1, ac2);
}
public static void findSubstitution(ITerm t1, ITerm t2, TermSubstitutionBinding substitution) throws UnificationException
{
if (t1.equals(t2)) {
return; // if both terms are equal then ignore them.
}
if (t1 instanceof IConstant && t2 instanceof IConstant) {
findSubstitution(TermUtils.asConstant(t1), TermUtils.asConstant(t2), substitution);
}
if (t1 instanceof IFunction && t2 instanceof IFunction) {
findSubstitution(TermUtils.asFunction(t1), TermUtils.asFunction(t2), substitution);
}
if (t1 instanceof IUriTemplate && t2 instanceof IUriReference) {
findSubstitution((IUriTemplate) t1, TermUtils.asUriReference(t2), substitution);
}
if (t1 instanceof IUriReference && t2 instanceof IUriTemplate) {
findSubstitution((IUriTemplate) t2, TermUtils.asUriReference(t1), substitution);
}
// Create a substitution unifier between variable and term
TermSubstitutionBinding sigma = TermSubstitutionBinding.createEmptyBinding();
if (t2 instanceof IVariable) {
sigma.put(TermUtils.asVariable(t2), t1);
}
else if (t1 instanceof IVariable) {
sigma.put(TermUtils.asVariable(t1), t2);
}
// Do substitution unifier composition.
substitution.compose(sigma);
}
public static void findSubstitution(IConstant c1, IConstant c2, TermSubstitutionBinding substitution) throws UnificationException
{
if (!c1.equals(c2)) {
String message = String.format("Constants are not equal \"%s\" and \"%s\"", c1.getLexicalValue(), c2.getLexicalValue()); //$NON-NLS-1$
throw new UnificationException(message);
}
}
public static void findSubstitution(IFunction f1, IFunction f2, TermSubstitutionBinding substitution) throws UnificationException
{
/*
* The fastest way to ignore finding the unifier is to compare both functions syntactically. This
* checking will throw UnificationException if the two functions are not similar.
*/
checkSyntacticalUnification(f1, f2);
IFunction fc1 = TermUtils.copy(f1);
IFunction fc2 = TermUtils.copy(f2);
for (int i = 0; i < fc1.getArity(); i++) {
final ITerm p1 = fc1.getParameter(i);
final ITerm p2 = fc2.getParameter(i);
findSubstitution(p1, p2, substitution);
fc1.apply(substitution);
fc2.apply(substitution);
}
checkOverflowUnification(fc1, fc2);
}
public static void findSubstitution(IUriTemplate uriTemplate, IUriReference uriReference, TermSubstitutionBinding substitution) throws UnificationException
{
String templateString = uriTemplate.getTemplateString();
String templateRegex = templateString.replaceAll("\\{\\d+\\}", "(.*)"); //$NON-NLS-1$
Pattern templatePattern = Pattern.compile(templateRegex);
List<ITerm> parameters = new ArrayList<ITerm>();
Matcher m = templatePattern.matcher(uriReference.getLexicalValue());
if (m.matches()) {
for (int i = 1; i <= m.groupCount(); i++) {
String value = m.group(i); // value comes from pattern matching
String datatype = uriTemplate.getParameter(i-1).getDatatype(); // datatype comes from template function
parameters.add(TermUtils.makeTypedLiteral(value, datatype)); //$NON-NLS-1$
}
}
IUriTemplate u2 = MappingObjectFactory.getInstance().createUriTemplate(templateString, parameters);
findSubstitution(uriTemplate, u2, substitution);
}
private static void checkSyntacticalUnification(ISignature s1, ISignature s2) throws UnificationException
{
if (!s1.isEquivalent(s2)) {
throw new UnificationException("Signatures are not equal"); //$NON-NLS-1$
}
}
private static void checkOverflowUnification(IExpressionObject a1, IExpressionObject a2) throws UnificationException
{
try {
a1.toString();
a2.toString();
}
catch (StackOverflowError e) {
throw new UnificationException("Infinite loop in unification", e); //$NON-NLS-1$
}
}
private static IAtom copy(IAtom atom)
{
return (IAtom) Serializer.copy(atom);
}
}