TermRenderer.java
/*******************************************************************************
* Copyright (c) 2025 Eclipse RDF4J contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
package org.eclipse.rdf4j.queryrender.sparql.util;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.eclipse.rdf4j.model.BNode;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Literal;
import org.eclipse.rdf4j.model.Triple;
import org.eclipse.rdf4j.model.Value;
import org.eclipse.rdf4j.model.vocabulary.XSD;
import org.eclipse.rdf4j.queryrender.sparql.PrefixIndex;
import org.eclipse.rdf4j.queryrender.sparql.PrefixIndex.PrefixHit;
/** Shared rendering helpers for IRIs and RDF4J Values. */
public final class TermRenderer {
private TermRenderer() {
}
public static String convertIRIToString(final IRI iri, final PrefixIndex index, final boolean usePrefixCompaction) {
final String s = iri.stringValue();
if (usePrefixCompaction) {
final PrefixHit hit = index.longestMatch(s);
if (hit != null) {
final String local = s.substring(hit.namespace.length());
if (SparqlNameUtils.isPNLocal(local)) {
return hit.prefix + ":" + local;
}
}
}
return "<" + s + ">";
}
public static String convertValueToString(final Value val, final PrefixIndex index,
final boolean usePrefixCompaction) {
if (val instanceof IRI) {
return convertIRIToString((IRI) val, index, usePrefixCompaction);
} else if (val instanceof Literal) {
final Literal lit = (Literal) val;
if (lit.getLanguage().isPresent()) {
return "\"" + TextEscapes.escapeLiteral(lit.getLabel()) + "\"@" + lit.getLanguage().get();
}
final IRI dt = lit.getDatatype();
final String label = lit.getLabel();
if (XSD.BOOLEAN.equals(dt)) {
return ("1".equals(label) || "true".equalsIgnoreCase(label)) ? "true" : "false";
}
if (XSD.INTEGER.equals(dt)) {
try {
return new BigInteger(label).toString();
} catch (NumberFormatException ignore) {
}
}
if (XSD.DECIMAL.equals(dt)) {
try {
return new BigDecimal(label).toPlainString();
} catch (NumberFormatException ignore) {
}
}
if (dt != null && !XSD.STRING.equals(dt)) {
return "\"" + TextEscapes.escapeLiteral(label) + "\"^^"
+ convertIRIToString(dt, index, usePrefixCompaction);
}
return "\"" + TextEscapes.escapeLiteral(label) + "\"";
} else if (val instanceof BNode) {
return "_:" + ((BNode) val).getID();
} else if (val instanceof Triple) {
Triple t = (Triple) val;
// Render components recursively; nested triples are allowed.
String s = convertValueToString(t.getSubject(), index, usePrefixCompaction);
String p = convertValueToString(t.getPredicate(), index, usePrefixCompaction);
String o = convertValueToString(t.getObject(), index, usePrefixCompaction);
return "<<" + s + " " + p + " " + o + ">>";
}
return "\"" + TextEscapes.escapeLiteral(String.valueOf(val)) + "\"";
}
}