This is a question about something that I am not sure how to solve in Java. I want to make triple statements based on three types of data, URI, String or Literal, each type is encoded differently. I have written encode methods that accept these types.
public static String makeStatement(URI subject, URI predicate, String object) {
return " " + encode(subject) + " " + encode(predicate) + " " + encode(object) + ".\n";
}
public static String makeStatement(String subject, URI predicate, String object) {
return " " + encode(subject) + " " + encode(predicate) + " " + encode(object) + ".\n";
}
public static String makeStatement(URI subject, URI predicate, Literal object) {
return " " + encode(subject) + " " + encode(predicate) + " " + encode(object) + ".\n";
}
private static String encode(String binding) {
return "?" + binding;
}
private static String encode(URI uri) {
return "<" + uri.stringValue() + ">";
}
private static String encode(Literal literal) {
return "\"" + literal.stringValue() + "\"" + literal.getDatatype();
}
But as I can accept any combination of these types this would require 9 makeStatement functions, which are basically doing the same thing and that seems like a bad idea, especially since it might be possible I want to add another type later on.
Normally I would answer such a question with the suggestion to create a superClass, but I cannot edit String, URI and Literal. Another option would be to define
public static String makeStatement(Object subject, Object predicate, Object object) {
String encodedSubject = "", encodedPredicate = "", encodedObject = "";
if (subject.getClass().equals(URI.class)) {
encodedSubject = encode((URI) subject);
}
return " " + encode(encodedSubject) + " " + encode(encodedPredicate) + " " + encode(encodedObject) + ".\n";
}
and then check the classes for each argument, but I consider this not very elegant. Another suggestion would be to define something like makeStatement(URI subjectURI, String subjectString, Literal subjectLiteral, URI predicateURI.. etc) and then check which arguments are null and go from there, but that would mean typing a lot of nulls when I call the function. A third option would be https://stackoverflow.com/a/12436592/1014666, but again this require quite some extra typing when calling the makeStatement function.
Any suggestions?
You can use a builder pattern:
public class StatementMaker {
private static String encode(String binding) {
return "?" + binding;
}
private static String encode(URI uri) {
return "<" + uri.stringValue() + ">";
}
private static String encode(Literal literal) {
return "\"" + literal.stringValue() + "\"" + literal.getDatatype();
}
public static Statement from(String b) {
return new Statement(encode(b));
}
public static Statement from(URI b) {
return new Statement(encode(b));
}
public static Statement from(Literal b) {
return new Statement(encode(b));
}
public static class Statement {
private StringBuilder buf;
private Statement(String s) {
buf = new StringBuilder(" ");
buf.append(s);
}
public Statement with(String s) {
buf.append(" ").append(encode(b));
return this;
}
public Statement with(URI s) {
buf.append(" ").append(encode(b));
return this;
}
public Statement with(Literal s) {
buf.append(" ").append(encode(b));
return this;
}
public String toString() {
return buf.toString() + ".\n";
}
}
}
You can now construct statement as such:
StatementMaker.from(subject).with(predicate).with(object).toString()
In code that needs the statements you can shorten the code further with static import:
import static my.package.StatementMaker.from;
Then the statement is reduced to:
from(subject).with(predicate).with(object).toString()
You can add 3 more methods to inner class:
public static class Statement {
private StringBuilder buf;
private Statement(String s) {
buf = new StringBuilder(" ");
buf.append(s);
}
public Statement with(String s) {
buf.append(" ").append(encode(b));
return this;
}
public Statement with(URI s) {
buf.append(" ").append(encode(b));
return this;
}
public Statement with(Literal s) {
buf.append(" ").append(encode(b));
return this;
}
public String and(String s) {
buf.append(" ").append(encode(b));
return buf.toString() + ".\n";
}
public String and(URI s) {
buf.append(" ").append(encode(b));
return buf.toString() + ".\n";
}
public String and(Literal s) {
buf.append(" ").append(encode(b));
return buf.toString() + ".\n";
}
public String toString() {
return buf.toString() + ".\n";
}
}
Then you can use avoid a toString()
call like this:
String statement = from(subject).with(predicate).and(object);
Method overloading works great if there's only a few options. What you have here is a bit obsessive. You don't need to have all the options if there's an easy way to convert from one to another.
So forget about having every possible choice and make the most used ones available.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With