/*
* 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.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.obidea.semantika.expression.base.IFunction;
import com.obidea.semantika.expression.base.ITerm;
import com.obidea.semantika.expression.base.IVariable;
import com.obidea.semantika.expression.base.TermUtils;
public class TermSubstitutionBinding implements ISubstitutionBinding<IVariable, ITerm>
{
private final Map<IVariable, ITerm> mBindings = new HashMap<IVariable, ITerm>();
/**
* Creates a blank unifier binding.
*/
public TermSubstitutionBinding()
{
// NO-OP
}
public static TermSubstitutionBinding createEmptyBinding()
{
return new TermSubstitutionBinding();
}
@Override
public void put(IVariable v, ITerm t)
{
mBindings.put(v, t);
}
@Override
public void remove(IVariable v)
{
mBindings.remove(v);
}
/**
* Get all variables in this binding.
*
* @return a set of all variables.
*/
@Override
public Set<IVariable> getVariables()
{
return mBindings.keySet();
}
/**
* Computes substitution composition.
* <p>
* Let <code>theta = {u_1/s_1, ..., u_m/s_m}</code> and
* <code>sigma = {v_1/t_1, ..., v_n/t_n}</code> be substitutions, then the
* composition <code>theta-sigma</code> of <code>theta</code> and
* <code>sigma</code> is the substitutions obtained from the set:
*
* <pre>
* theta-sigma = {u_1/s_1#sigma, ..., u_m/s_m#sigma, v_1/t_1, ..., v_n/t_n}</pre>
*
* by deleting any binding <code>u_i/s_i#sigma</code> for which
* <code>u_i = s_i#sigma</code> and deleting any binding <code>v_j/t_j</code>
* for which <code>v_j element in {u_i, ..., u_m}</code>
* <p>
*
* @param other
* The other substitution to be composed with this substitution.
*/
public void compose(TermSubstitutionBinding other)
{
/*
* Applying sigma to theta substitution such that {u_1/s_1#sigma, ...,
* u_m/s_m#sigma}
*/
Set<IVariable> thetaVariables = getVariables();
for (final IVariable u : thetaVariables) {
ITerm s = mBindings.get(u);
if (s instanceof IFunction) {
// Apply the substitution recursively
IFunction function = (IFunction) s;
function.apply(other);
}
else if (s instanceof IVariable) {
// Replace the variable accordingly if it is bounded
IVariable var = (IVariable) s;
if (other.isBound(var)) {
mBindings.put(u, other.getTerm(var));
}
}
}
/*
* Insert sigma substitution to the composition result, if its variable
* doesn't contain in theta substitution, i.e., v_j element of {u_i, ..., u_m}
*/
for (final IVariable v : other.getVariables()) {
if (!thetaVariables.contains(v)) { //
mBindings.put(v, other.getTerm(v));
}
}
/*
* Delete bindings in the composition result if the variable and term are
* equals, i.e., u_i = s_i#sigma
*/
Object[] binds = mBindings.entrySet().toArray();
for (int i = 0; i < binds.length; i++) {
@SuppressWarnings("unchecked")
Entry<IVariable, ITerm> bind = (Entry<IVariable, ITerm>) binds[i];
final IVariable var = bind.getKey();
final ITerm term = bind.getValue();
if (var.equals(term)) {
mBindings.remove(var);
}
}
}
@Override
public ITerm replace(IVariable v)
{
return mBindings.get(v);
}
public ITerm getTerm(IVariable v)
{
return mBindings.get(v);
}
@Override
public boolean isBound(IVariable v)
{
return mBindings.containsKey(v);
}
@Override
public boolean isEmpty()
{
return mBindings.isEmpty();
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
boolean needComma = false;
for (IVariable v : getVariables()) {
if (needComma) {
sb.append(", ");
}
sb.append(TermUtils.toString(v));
sb.append("/");
sb.append(TermUtils.toString(mBindings.get(v)));
needComma = true;
}
return "{" + sb.toString() + "}";
}
}