/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.jena.reasoner.rulesys; import java.io.*; import java.util.*; import org.apache.jena.datatypes.xsd.XSDDateTime ; import org.apache.jena.graph.* ; import org.apache.jena.graph.impl.* ; import org.apache.jena.rdf.model.* ; import org.apache.jena.reasoner.Finder ; import org.apache.jena.reasoner.IllegalParameterException ; import org.apache.jena.reasoner.TriplePattern ; import org.apache.jena.util.FileUtils ; import org.apache.jena.util.iterator.ClosableIterator ; import org.apache.jena.vocabulary.RDF ; /** * A small random collection of utility functions used by the rule systems. */ public class Util { /** * Check whether a Node is a numeric (integer) value */ public static boolean isNumeric(Node n) { return n.isLiteral() && n.getLiteralValue() instanceof Number; } /** * Return the integer value of a literal node */ public static int getIntValue(Node n) { return ((Number)n.getLiteralValue()).intValue(); } /** * Compare two numeric nodes. * @param n1 the first numeric valued literal node * @param n2 the second numeric valued literal node * @return -1 if n1 is less than n2, 0 if n1 equals n2 and +1 if n1 greater than n2 * @throws ClassCastException if either node is not numeric */ public static int compareNumbers(Node n1, Node n2) { if (n1.isLiteral() && n2.isLiteral()) { Object v1 = n1.getLiteralValue(); Object v2 = n2.getLiteralValue(); if (v1 instanceof Number && v2 instanceof Number) { if (v1 instanceof Float || v1 instanceof Double || v2 instanceof Float || v2 instanceof Double) { double d1 = ((Number)v1).doubleValue(); double d2 = ((Number)v2).doubleValue(); return (d1 < d2) ? -1 : ( (d1 == d2) ? 0 : +1 ); } else { long l1 = ((Number)v1).longValue(); long l2 = ((Number)v2).longValue(); return (l1 < l2) ? -1 : ( (l1 == l2) ? 0 : +1 ); } } } throw new ClassCastException("Non-numeric literal in compareNumbers"); } /** * Check whether a Node is an Instant (DateTime) value */ public static boolean isInstant(Node n) { if (n.isLiteral()) { Object o = n.getLiteralValue(); return (o instanceof XSDDateTime); } else { return false; } } /** * Compare two time Instant nodes. * @param n1 the first time instant (XSDDateTime) valued literal node * @param n2 the second time instant (XSDDateTime) valued literal node * @return -1 if n1 is less than n2, 0 if n1 equals n2 and +1 if n1 greater than n2 * @throws ClassCastException if either not is not numeric */ public static int compareInstants(Node n1, Node n2) { if (n1.isLiteral() && n2.isLiteral()) { Object v1 = n1.getLiteralValue(); Object v2 = n2.getLiteralValue(); if (v1 instanceof XSDDateTime && v2 instanceof XSDDateTime) { XSDDateTime a = (XSDDateTime) v1; XSDDateTime b = (XSDDateTime) v2; return a.compare(b); } } throw new ClassCastException("Non-numeric literal in compareNumbers"); } /** * General order comparator for typed literal nodes, works for all numbers and * for date times. * */ // Thanks to Bradley Schatz (Bradley@greystate.com) for the original suggestions // for datetime comparison. This code is a rewrite based on those suggestions. public static int compareTypedLiterals(Node n1, Node n2) { if (n1.isLiteral() && n2.isLiteral()) { Object v1 = n1.getLiteralValue(); Object v2 = n2.getLiteralValue(); if (v1 instanceof XSDDateTime && v2 instanceof XSDDateTime) { XSDDateTime a = (XSDDateTime) v1; XSDDateTime b = (XSDDateTime) v2; return a.compare(b); } else { if (v1 instanceof Number && v2 instanceof Number) { if (v1 instanceof Float || v1 instanceof Double || v2 instanceof Float || v2 instanceof Double) { double d1 = ((Number)v1).doubleValue(); double d2 = ((Number)v2).doubleValue(); return (d1 < d2) ? -1 : ( (d1 == d2) ? 0 : +1 ); } else { long l1 = ((Number)v1).longValue(); long l2 = ((Number)v2).longValue(); return (l1 < l2) ? -1 : ( (l1 == l2) ? 0 : +1 ); } } } } throw new ClassCastException("Compare typed literals can only compare numbers and datetimes"); } /** * Test if two literals are comparable by an order operator (both numbers or both times) */ public static boolean comparable(Node n1, Node n2) { return (isNumeric(n1) && isNumeric(n2)) || (isInstant(n1) && isInstant(n2)); } /** * Helper - returns the (singleton) value for the given property on the given * root node in the data graph. */ public static Node getPropValue(Node root, Node prop, Finder context) { return doGetPropValue(context.find(new TriplePattern(root, prop, null))); } /** * Helper - returns the (singleton) value for the given property on the given * root node in the data graph. */ public static Node getPropValue(Node root, Node prop, Graph context) { return doGetPropValue(context.find(root, prop, null)); } /** * Helper - returns the (singleton) value for the given property on the given * root node in the data graph. */ public static Node getPropValue(Node root, Node prop, RuleContext context) { return doGetPropValue(context.find(root, prop, null)); } /** * Internall implementation of all the getPropValue variants. */ private static Node doGetPropValue(ClosableIterator<Triple> it) { Node result = null; if (it.hasNext()) { result = it.next().getObject(); } it.close(); return result; } /** * Convert an (assumed well formed) RDF list to a java list of Nodes * @param root the root node of the list * @param context the graph containing the list assertions */ public static List<Node> convertList(Node root, RuleContext context) { return convertList(root, context, new LinkedList<Node>()); } /** * Convert an (assumed well formed) RDF list to a java list of Nodes */ private static List<Node> convertList( Node node, RuleContext context, List<Node> sofar ) { if (node == null || node.equals(RDF.nil.asNode())) return sofar; Node next = getPropValue(node, RDF.first.asNode(), context); if (next != null) { sofar.add(next); return convertList(getPropValue(node, RDF.rest.asNode(), context), context, sofar); } else { return sofar; } } /** * Construct a new integer valued node */ public static Node makeIntNode(int value) { return NodeFactory.createLiteral(LiteralLabelFactory.createTypedLiteral( value )); } /** * Construct a new long valued node */ public static Node makeLongNode(long value) { if (value > Integer.MAX_VALUE) { return NodeFactory.createLiteral(LiteralLabelFactory.createTypedLiteral( value )); } else { return NodeFactory.createLiteral(LiteralLabelFactory.createTypedLiteral( (int) value )); } } /** * Construct a new double valued node */ public static Node makeDoubleNode(double value) { return NodeFactory.createLiteral(LiteralLabelFactory.createTypedLiteral( value )); } /** * Construct an RDF list from the given array of nodes and assert it * in the graph returning the head of the list. */ public static Node makeList(Node[] nodes, Graph graph) { return doMakeList(nodes, 0, graph); } /** * Internals of makeList. */ private static Node doMakeList(Node[] nodes, int next, Graph graph) { if (next < nodes.length) { Node listNode = NodeFactory.createBlankNode(); graph.add(new Triple(listNode, RDF.Nodes.first, nodes[next])); graph.add(new Triple(listNode, RDF.Nodes.rest, doMakeList(nodes, next+1, graph))); return listNode; } else { return RDF.Nodes.nil; } } /** * Open a resource file and read it all into a single string. * Treats lines starting with # as comment lines, as per stringFromReader */ public static Rule.Parser loadRuleParserFromResourceFile( String filename ) { return Rule.rulesParserFromReader( FileUtils.openResourceFile( filename ) ); } /** * Open a file defined by a URL and read all of it into a single string. * If the URL fails it will try a plain file name as well. */ public static String loadURLFile(String urlStr) throws IOException { try ( BufferedReader dataReader = FileUtils.readerFromURL( urlStr ); StringWriter sw = new StringWriter(1024); ) { char buff[] = new char[1024]; while (dataReader.ready()) { int l = dataReader.read(buff); if (l <= 0) break; sw.write(buff, 0, l); } return sw.toString(); } } /** * Helper method - extracts the truth of a boolean configuration * predicate. * @param predicate the predicate to be tested * @param configuration the configuration node * @return null if there is no setting otherwise a Boolean giving the setting value */ public static Boolean checkBinaryPredicate(Property predicate, Resource configuration) { StmtIterator i = configuration.listProperties(predicate); if (i.hasNext()) { return i.nextStatement().getObject().toString().equalsIgnoreCase( "true" ); } else { return null; } } /** * Helper method - extracts the value of an integer configuration * predicate. * @param predicate the predicate to be tested * @param configuration the configuration node * @return null if there is no such configuration parameter otherwise the value as an integer */ public static Integer getIntegerPredicate(Property predicate, Resource configuration) { StmtIterator i = configuration.listProperties(predicate); if (i.hasNext()) { RDFNode lit = i.nextStatement().getObject(); if (lit instanceof Literal) { return ( (Literal) lit ).getInt(); } } return null; } /** * Convert the value of a boolean configuration parameter to a boolean value. * Allows the value to be specified using a String or Boolean. * @param parameter the configuration property being set (to help with error messages) * @param value the parameter value * @return the converted value * @throws IllegalParameterException if the value can't be converted */ public static boolean convertBooleanPredicateArg(Property parameter, Object value) { if (value instanceof Boolean) { return ((Boolean)value).booleanValue(); } else if (value instanceof String) { return ((String)value).equalsIgnoreCase("true"); } else { throw new IllegalParameterException("Illegal type for " + parameter + " setting - use a Boolean"); } } /** * Convert the value of an integer configuration parameter to an int value. * Allows the value to be specified using a String or Number. * @param parameter the configuration property being set (to help with error messages) * @param value the parameter value * @return the converted value * @throws IllegalParameterException if the value can't be converted */ public static int convertIntegerPredicateArg(Property parameter, Object value) { if (value instanceof Number) { return ((Number)value).intValue(); } else if (value instanceof String) { try { return Integer.parseInt((String)value); } catch (NumberFormatException e) { throw new IllegalParameterException("Illegal type for " + parameter + " setting - use an integer"); } } else { throw new IllegalParameterException("Illegal type for " + parameter + " setting - use an integer"); } } /** * Replace the value for a given parameter on the resource by a new value. * @param config the resource whose values are to be updated * @param parameter a predicate defining the parameter to be set * @param value the new value */ public static void updateParameter(Resource config, Property parameter, Object value) { for (StmtIterator i = config.listProperties(parameter); i.hasNext(); ) { i.next(); i.remove(); } config.addProperty( parameter, value.toString() ); } }