eionet.cr.util.sesame.ResultCompareUtil.java Source code

Java tutorial

Introduction

Here is the source code for eionet.cr.util.sesame.ResultCompareUtil.java

Source

/*
 * The contents of this file are subject to the Mozilla Public
 * License Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code is Content Registry 3
 *
 * The Initial Owner of the Original Code is European Environment
 * Agency. Portions created by TripleDev or Zero Technologies are Copyright
 * (C) European Environment Agency.  All Rights Reserved.
 *
 * Contributor(s):
 *        jaanus
 */

package eionet.cr.util.sesame;

import info.aduna.iteration.Iterations;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.datatype.XMLGregorianCalendar;

import org.apache.commons.lang.StringUtils;
import org.openrdf.model.BNode;
import org.openrdf.model.Literal;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.model.datatypes.XMLDatatypeUtil;
import org.openrdf.model.vocabulary.XMLSchema;
import org.openrdf.query.Binding;
import org.openrdf.query.BindingSet;
import org.openrdf.query.QueryEvaluationException;
import org.openrdf.query.TupleQueryResult;

import eionet.cr.common.CRException;

/**
 * Utility methods for comparing result sets of Sesame query results.
 *
 * @author jaanus
 */
public class ResultCompareUtil {

    /**
     * Disable utility class constructor.
     */
    private ResultCompareUtil() {
        // Empty constructor.
    }

    /**
     *
     * @param tqr1
     * @param tqr2
     * @return
     * @throws QueryEvaluationException
     */
    public static boolean equals(TupleQueryResult tqr1, TupleQueryResult tqr2) throws QueryEvaluationException {
        List<BindingSet> list1 = Iterations.asList(tqr1);
        List<BindingSet> list2 = Iterations.asList(tqr2);

        // Compare the number of statements in both sets
        if (list1.size() != list2.size()) {
            return false;
        }

        return matchBindingSets(list1, list2);
    }

    /**
     *
     * @param queryResult1
     * @param queryResult2
     * @return
     */
    private static boolean matchBindingSets(List<? extends BindingSet> queryResult1,
            Iterable<? extends BindingSet> queryResult2) {
        return matchBindingSets(queryResult1, queryResult2, new HashMap<BNode, BNode>(), 0);
    }

    /**
     *
     * @param queryResult1
     * @param queryResult2
     * @param bNodeMapping
     * @param idx
     * @return
     */
    private static boolean matchBindingSets(List<? extends BindingSet> queryResult1,
            Iterable<? extends BindingSet> queryResult2, Map<BNode, BNode> bNodeMapping, int idx) {

        boolean result = false;

        if (idx < queryResult1.size()) {
            BindingSet bs1 = queryResult1.get(idx);

            if (idx % 5000 == 0) {
                System.out.println("checking idx #" + idx);
            }
            List<BindingSet> matchingBindingSets = findMatchingBindingSets(bs1, queryResult2, bNodeMapping);

            if (matchingBindingSets.isEmpty()) {
                System.out.println("Result2 has no match for result1's BindingSet #" + idx + ":\n" + toString(bs1));
            }

            for (BindingSet bs2 : matchingBindingSets) {
                // Map bNodes in bs1 to bNodes in bs2
                Map<BNode, BNode> newBNodeMapping = new HashMap<BNode, BNode>(bNodeMapping);

                for (Binding binding : bs1) {
                    if (binding.getValue() instanceof BNode) {
                        newBNodeMapping.put((BNode) binding.getValue(), (BNode) bs2.getValue(binding.getName()));
                    }
                }

                // FIXME: this recursive implementation has a high risk of
                // triggering a stack overflow

                // Enter recursion
                result = matchBindingSets(queryResult1, queryResult2, newBNodeMapping, idx + 1);

                if (result) {
                    // models match, look no further
                    break;
                }
            }
        } else {
            // All statements have been mapped successfully
            result = true;
        }

        return result;
    }

    /**
     *
     * @param bs
     * @return
     */
    private static String toString(BindingSet bs) {

        ArrayList<String> list = new ArrayList<String>();
        for (String name : bs.getBindingNames()) {
            list.add(bs.getValue(name).stringValue());
        }
        return list.toString();
    }

    /**
     *
     * @param st
     * @param model
     * @param bNodeMapping
     * @return
     */
    private static List<BindingSet> findMatchingBindingSets(BindingSet st, Iterable<? extends BindingSet> model,
            Map<BNode, BNode> bNodeMapping) {

        List<BindingSet> result = new ArrayList<BindingSet>();

        for (BindingSet modelSt : model) {
            if (bindingSetsMatch(st, modelSt, bNodeMapping)) {
                // All components possibly match
                result.add(modelSt);
            }
        }

        return result;
    }

    /**
     *
     * @param bs1
     * @param bs2
     * @param bNodeMapping
     * @return
     */
    private static boolean bindingSetsMatch(BindingSet bs1, BindingSet bs2, Map<BNode, BNode> bNodeMapping) {

        if (bs1.size() != bs2.size()) {
            return false;
        }

        for (Binding binding1 : bs1) {
            Value value1 = binding1.getValue();
            Value value2 = bs2.getValue(binding1.getName());

            if (value1 instanceof BNode && value2 instanceof BNode) {
                BNode mappedBNode = bNodeMapping.get(value1);

                if (mappedBNode != null) {
                    // bNode 'value1' was already mapped to some other bNode
                    if (!value2.equals(mappedBNode)) {
                        // 'value1' and 'value2' do not match
                        return false;
                    }
                } else {
                    // 'value1' was not yet mapped, we need to check if 'value2' is a
                    // possible mapping candidate
                    if (bNodeMapping.containsValue(value2)) {
                        // 'value2' is already mapped to some other value.
                        return false;
                    }
                }
            } else {

                if (!StringUtils.equals(value1.stringValue(), value2.stringValue())) {
                    return false;
                }
                // values are not (both) bNodes
                if (value1 instanceof Literal && value2 instanceof Literal) {
                    // do literal value-based comparison for supported datatypes
                    Literal leftLit = (Literal) value1;
                    Literal rightLit = (Literal) value2;

                    URI dt1 = leftLit.getDatatype();
                    URI dt2 = rightLit.getDatatype();

                    if (dt1 != null && dt2 != null && dt1.equals(dt2)
                            && XMLDatatypeUtil.isValidValue(leftLit.getLabel(), dt1)
                            && XMLDatatypeUtil.isValidValue(rightLit.getLabel(), dt2)) {
                        Integer compareResult = null;
                        if (dt1.equals(XMLSchema.DOUBLE)) {
                            compareResult = Double.compare(leftLit.doubleValue(), rightLit.doubleValue());
                        } else if (dt1.equals(XMLSchema.FLOAT)) {
                            compareResult = Float.compare(leftLit.floatValue(), rightLit.floatValue());
                        } else if (dt1.equals(XMLSchema.DECIMAL)) {
                            compareResult = leftLit.decimalValue().compareTo(rightLit.decimalValue());
                        } else if (XMLDatatypeUtil.isIntegerDatatype(dt1)) {
                            compareResult = leftLit.integerValue().compareTo(rightLit.integerValue());
                        } else if (dt1.equals(XMLSchema.BOOLEAN)) {
                            Boolean leftBool = Boolean.valueOf(leftLit.booleanValue());
                            Boolean rightBool = Boolean.valueOf(rightLit.booleanValue());
                            compareResult = leftBool.compareTo(rightBool);
                        } else if (XMLDatatypeUtil.isCalendarDatatype(dt1)) {
                            XMLGregorianCalendar left = leftLit.calendarValue();
                            XMLGregorianCalendar right = rightLit.calendarValue();

                            compareResult = left.compare(right);
                        }

                        if (compareResult != null) {
                            if (compareResult.intValue() != 0) {
                                return false;
                            }
                        } else if (!value1.equals(value2)) {
                            return false;
                        }
                    } else if (!value1.equals(value2)) {
                        return false;
                    }
                } else if (!value1.equals(value2)) {
                    return false;
                }
            }
        }

        return true;
    }

    /**
     *
     * @param result1
     * @param result2
     * @return
     * @throws Exception
     */
    private static boolean compare(TupleQueryResult result1, TupleQueryResult result2) throws Exception {

        int numOfUnequalRows = 0;
        int i = -1;
        boolean hasNext1 = false;
        boolean hasNext2 = false;
        do {
            i++;

            if (i % 5000 == 0) {
                System.out.println("At row#" + i);
            }

            hasNext1 = result1.hasNext();
            hasNext2 = result2.hasNext();
            if (hasNext1 && hasNext2) {

                BindingSet bindingSet1 = result1.next();
                BindingSet bindingSet2 = result2.next();
                Set<String> names1 = bindingSet1.getBindingNames();
                if (i == 0) {

                    Set<String> names2 = bindingSet1.getBindingNames();
                    if (!names1.equals(names2)) {
                        throw new Exception("Binding sets not equal:\n" + names1 + "\n" + names2);
                    }
                }

                ArrayList<String> values1 = new ArrayList<String>();
                ArrayList<String> values2 = new ArrayList<String>();
                for (String name : names1) {

                    Value value1 = bindingSet1.getValue(name);
                    Value value2 = bindingSet2.getValue(name);

                    String str1 = value1.stringValue();
                    String str2 = value2.stringValue();

                    values1.add("0.0".equals(str1) ? "0" : str1);
                    values2.add("0.0".equals(str2) ? "0" : str2);
                }

                if (!values1.equals(values2)) {
                    numOfUnequalRows++;
                    System.out.println("Row #" + i + " is different:\n1: " + values1 + "\n2: " + values2);
                    return false;
                }
            }
        } while (hasNext1 && hasNext2);

        System.out.println("numOfUnequalRows = " + numOfUnequalRows);

        if (!hasNext1 && hasNext2) {
            throw new CRException("At row#" + i + " result2 has next, but result1 does not!");
        } else if (hasNext1 && !hasNext2) {
            throw new CRException("At row#" + i + " result1 has next, but result2 does not!");
        }

        return numOfUnequalRows == 0;
    }
}