Does StringTemplate 4 require template files on disk? - java

I am trying to find an HTML template solution for a Java web application. I need the flexibility to load templates from whatever sources I choose as most of them will likely be in a custom database. In my search I stumbled upon StringTemplate 4, however all of the examples I see require that the user put template files on disk.
I have noticed that STGroup can be instantiated without specifying a file or directory, however using the defineTemplate method does not seem to be a substitute for using file based templates. Unfortunately in all my tests with defineTemplate I have failed to get attributes to work. This all feels like I'm guessing in the dark.
Is StringTemplate the right library for this? Is there another one that would work better?
I'm starting to consider developing my own.

I figured it out...
import org.stringtemplate.v4.*;
import net.sf.json.*;
class STExampleApp {
public static void main(String args[]) {
String template = "decl(type, name, value) ::= \"<type> <name><init(value)>;\"\n"
+ "init(v) ::= \"<if(v)> = <v><endif>\"";
STGroup views = new STGroupString(template);
ST decl = views.getInstanceOf("decl");
decl.add("type", "int");
decl.add("name", "x");
decl.add("value", 12);
System.out.println(decl.render());
}
}
No file loading necessary. I learned this from: How to format decimal numbers with StringTemplate (ST) in Java?

I would just pass the template to a ST() constructor like this:
#Test public void testNullAttr() throws Exception {
String template = "hi <name>!";
ST st = new ST(template);
String expected =
"hi !";
String result = st.render();
assertEquals(expected, result);
}

Related

How to store function handles from ScriptManager for later usage?

tl;dr:
How do/can I store the function-handles of multiple js-functions in java for using them later? Currently I have two ideas:
Create multipe ScriptEngine instances, each containing one loaded function. Store them in a map by column, multiple entries per column in a list. Looks like a big overhead depending on how 'heavy' a ScriptEngine instance is...
Some Javascript solution to append methods of the same target field to an array. Dont know yet how to access that from the java-side, but also dont like it. Would like to keep the script files as stupid as possible.
var test1 = test1 || [];
test1.push(function(input) { return ""; });
???
Ideas or suggestions?
Tell me more:
I have a project where I have a directory containing script files (javascript, expecting more than hundred files, will grow in future). Those script files are named like: test1;toupper.js, test1;trim.js and test2;capitalize.js. The name before the semicolon is the column/field that the script will be process and the part after the semicolon is a human readable description what the file does (simplified example). So in this example there are two scripts that will be assigned to the "test1" column and one script to the "test2" column. The js-function template basically looks like:
function process(input) { return ""; };
My idea is, to load (and evaluate/compile) all script files at server-startup and then use the loaded functions by column when they are needed. So far, so good.
I can load/evaluate a single function with the following code. Example uses GraalVM, but should be reproducable with other languages too.
final ScriptEngine engine = new ScriptEngineManager().getEngineByName("graal.js");
final Invocable invocable = (Invocable) engine;
engine.eval("function process(arg) { return arg.toUpperCase(); };");
var rr0 = invocable.invokeFunction("process", "abc123xyz"); // rr0 = ABC123XYZ
But when I load/evaluate the next function with the same name, the previous one will be overwritten - logically, since its the same function name.
engine.eval("function process(arg) { return arg + 'test'; };");
var rr1 = invocable.invokeFunction("process", "abc123xyz"); // rr1 = abc123xyztest
This is how I would do it.
The recommended way to use Graal.js is via the polyglot API: https://www.graalvm.org/reference-manual/embed-languages/
Not the same probably would work with the ScriptEngine API, but here's the example using the polyglot API.
Wrap the function definition in ()
return the functions to Java
Not pictured, but you probably build a map from the column name to a list of functions to invoke on it.
Call the functions on the data.
import org.graalvm.polyglot.*;
import org.graalvm.polyglot.proxy.*;
public class HelloPolyglot {
public static void main(String[] args) {
System.out.println("Hello Java!");
try (Context context = Context.create()) {
Value toUpperCase = context.eval("js", "(function process(arg) { return arg.toUpperCase(); })");
Value concatTest = context.eval("js", "(function process(arg) { return arg + 'test'; })");
String text = "HelloWorld";
text = toUpperCase.execute(text).asString();
text = concatTest.execute(text).asString();
System.out.println(text);
}
}
}
Now, Value.execute() returns a Value, which I for simplicity coerce to a Java String with asString(), but you don't have to do that and you can operate on Value (here's the API for Value: https://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/Value.html).

Java - Better way to parse a RESTful resource URL

I'm new to developing web services in Java (previously I've done them in PHP and Ruby). I'm writing a resource that is of the following format:
<URL>/myService/<domain>/<app_name>/<system_name>
As you can see, I've got a three-level resource identifier, and I'm trying to figure out the best way to parse it. The application I'm adding this new service to doesn't make use of Jersey or any RESTful frameworks like that. Instead, it's just extending HttpServlet.
Currently they're following an algorithm like this:
Call request.getPathInfo()
Replace the "/" characters in the path info with "." characters
Use String.substring methods to extract individual pieces of information for this resource from the pathInfo string.
This doesn't seem very elegant to me, and I'm looking for a better way. I know that using the javax.ws.rs package makes this very easy (using #Path and #PathParam annotations), but using Jersey is probably not an option.
Using only the base HttpServletRequest object and standard Java libraries, is there a better way to parse this information than the method described above?
How about jersey UriTemplate?
import com.sun.jersey.api.uri.UriTemplate;
...
String path = "/foos/foo/bars/bar";
Map<String, String> map = new HashMap<String, String>();
UriTemplate template = new UriTemplate("/foos/{foo}/bars/{bar}");
if( template.match(path, map) ) {
System.out.println("Matched, " + map);
} else {
System.out.println("Not matched, " + map);
}
I've recently solved this issue in one of my applications. My URLs look like this.
/categories/{category}/subcategories/{subcategory}
My problem was that I wanted to map each url pattern with a Java class, so that I could call upon the correct class to render the data.
My application uses Netty, but the URL resolver doesn't use any third party libraries.
What this allows me to do is to parse the URL that is coming in from the browser, generate a map that has key-value pairs (in this case category, and subcategory), as well as instantiate the correct handler for each unique URL pattern. All in all only about 150 lines of Java code for the parsing, the application setup and the definition of the unique URL patterns.
You can view the code for the resolver in GitHub: https://github.com/joachimhs/Contentice/blob/master/Contentice.api/src/main/java/no/haagensoftware/contentice/util/URLResolver.java
UrlResolver.getValueForUrl will return a URLData with the information that you require about your URL:
https://github.com/joachimhs/Contentice/blob/master/Contentice.api/src/main/java/no/haagensoftware/contentice/data/URLData.java
Once this is setup, I can associate URLs with Netty Handlers:
this.urlResolver.addUrlPattern("/categories", CategoriesHandler.class);
this.urlResolver.addUrlPattern("/categories/{category}", CategoryHandler.class);
this.urlResolver.addUrlPattern("/categories/{category}/subcategories", SubCategoriesHandler.class);
this.urlResolver.addUrlPattern("/categories/{category}/subcategories/{subcategory}", SubCategoryHandler.class);
Inside my Handlers I can simply get the parameter map:
String category = null;
logger.info("parameterMap: " + getParameterMap());
if (getParameterMap() != null) {
category = getParameterMap().get("category");
}
I hope that helps :)
I had the same problem as you and, as I didn't find any suitable library, I decided to write URL-RESTify. You may use it or just take a look to write your own solution, it's a small project.
Jersey's UriTemplate mentioned in other answers is good, but it's a big library and it also includes many other dependency libraries.
Tiny solution with no dependency:
https://github.com/xitrum-framework/jauter
I believe first you need to create a framework for storing the REST method and class+method mappings in a property file or in memory data structue. Then write a top level servlet accepting all of your REST request. Depending on the URL starting from your context, you can try to fetch the mapping from your property file/in memory data structure to find out which class and which of its method need to be called. Then making use of reflection you can call the desired method. Take the method response and marshal it into the desired content-type format and send back to the servlet response output stream.
Implemented it myself (check the main method for example), just in case if you would want a custom implementation:
import lombok.AllArgsConstructor;
import lombok.NonNull;
import java.util.*;
public class Template {
final List<TemplateElement> templateElements = new ArrayList<>();
public static void main(String[] args) {
final Template template = new Template("/hello/{who}");
final Map<String, String> attributes = template.parse("/hello/world").get();
System.out.println(attributes.get("who")); // world
}
public Template(#NonNull final String template) {
validate(template);
final String[] pathElements = template.split("/");
for (final String element : pathElements) {
if (isAttribute(element)) {
final String elementName = element.substring(1, element.length() - 1); // exclude { and }
templateElements.add(new TemplateElement(ElementType.ATTRIBUTE, elementName));
} else {
templateElements.add(new TemplateElement(ElementType.FIXED, element));
}
}
}
public Optional<Map<String, String>> parse(#NonNull final String path) {
validate(path);
final String[] pathElements = path.split("/");
if (pathElements.length != templateElements.size()) return Optional.empty();
final Map<String, String> attributes = new HashMap<>();
// ignore the 0th element, it'll always be empty
for (int i = 1; i < templateElements.size(); i++) {
final String element = pathElements[i];
final TemplateElement templateElement = templateElements.get(i);
switch (templateElement.type) {
case FIXED:
if (!element.equals(templateElement.name)) return Optional.empty();
break;
case ATTRIBUTE:
attributes.put(templateElement.name, element);
break;
}
}
return Optional.of(attributes);
}
private void validate(#NonNull final String path) {
if (!path.startsWith("/"))
throw new RuntimeException("A template must start with /"); // a template must start with /
}
private boolean isAttribute(#NonNull final String str) {
return str.startsWith("{") && str.endsWith("}");
}
#AllArgsConstructor
class TemplateElement {
final ElementType type;
final String name;
}
enum ElementType {
FIXED, ATTRIBUTE
}
}
Please point out mistakes if any. Thanks.

Conditional code generation using StringTemplate

In my project, I have a class Device like this:
public class Device {
private Set<String> abilities = new HashSet<String>();
public Device(Set<String> abilities) {
this.abilities = abilities;
}
public Set<String> getAbilities() {
return abilities;
}
}
I am initializing this Device class with:
Set<String> device1Abilities = new HashSet<String>();
device1Abilities.add("BadgeReader");
device1Abilities.add("TemperatureSensor");
device1Abilities.add("xyz");
Device d1 = new Device(device1Abilities);
In my stringTemplateFile, I am retrieving abilities using
$device.abilities :{ sc | abilities.add("$sc$"); }$
which will generates following code =>
abilities.add("BadgeReader");
abilities.add("TemperatureSensor");
abilities.add("xyz");
Now, my requirement is ----- I do not want to generate this line of code:
abilities.add("xyz");
What condition should I specify in
$device.abilities :{ sc | abilities.add("$sc$"); }$
so that it does not generate that line?
That computation really belongs in the model so you should do the filtering of the list that you passed to the template. The template should not figure out which data to display. It should display the data that your model says it should display. hope this helps.
See here. You are using an anonymous sub-template abilities.add("$sc$");. Instead you can use a template call with sc as parameter. And there you can test on "xyz". Though maybe someone with more StringTemplate experience knows a shorter notation.

Pretty format of XML generated by Simple (Java Lib)

I am facing a problem regarding readability and cross-platform issues.
We are generating our UI on the fly, using certain classes and subclasses. For an example, I will use basic elements (not UI ones), but I think the "problem" should be obvious.
I am asking IF there is any configuration switch in Simple (http://simple.sourceforge.net/home.php) to achieve the desired result. As said, its about readability for another platform, if I would only have to target JAVA, I would not care about the look and feel of the XML.
So, in my example, I serialize a simple class, the result is:
<GuiElementExamples>
<LastCreated>2012-04-15 16:48:59.813 CEST</LastCreated>
<NonGuiObject>
<objectBase class="objects.single.simple.StringObject" _value="">
<_readonly>false</_readonly>
</objectBase>
<objectBase class="objects.single.simple.StringProperty">
<_items>
<object class="objects.single.simple.StringObject" _value="Label">
<_readonly>true</_readonly>
</object>
<object class="objects.single.simple.StringObject" _value="">
<_readonly>false</_readonly>
</object>
</_items>
<_readonly>false</_readonly>
</objectBase>
</NonGuiObject>
</GuiElementExamples>
What I would love to have (I will create it by hand as an example), is this:
<GuiElementExamples>
<LastCreated>2012-04-15 16:48:59.813 CEST</LastCreated>
<NonGuiObject>
<StringObject _value="">
<_readonly>false</_readonly>
</StringObject>
<StringProperty>
<_items>
<StringObject _value="Label">
<_readonly>true</_readonly>
</StringObject>
<StringObject _value="">
<_readonly>false</_readonly>
</StringObject>
</_items>
<_readonly>false</_readonly>
</StringProperty>
</NonGuiObject>
</GuiElementExamples>
I KNOW there will be NO clashes in class names, and one option I could use is a simple search and replace script, but maybe there is an option to configure "simple" in a way to export the stuff as shown above.
I am aware that there would be no way to DESERIALIZE stuff in the format above, without running e.g. the mentioned script before, because without the fully qualified class name, simple can't know which object to create..
Thanks for any help or workaround ideas,
Chris
Like always, after writing it, and going back to the docs, I found it :-)
It is the last entry on the tutorial page, in my example this does the trick:
EDIT This does not work with more then one element, dont ask me why EDIT
#ElementListUnion({#ElementList(entry = "StringObject", type = StringObject.class)})
private ArrayList<T> _items = new ArrayList<T>();
The solution I used is a little bit more complicated, but get the job done. I will post the source here to point you in the right direction (ObjectBase is the base class of all my objects I want to "rename" the way described above):
public class FormatVisitor implements Visitor
{
private static ILogger _logger = LogManager.GetLogger(FormatVisitor.class);
#Override
public void read(Type type, NodeMap<InputNode> strings) throws Exception
{
}
public void write(Type type, NodeMap<OutputNode> node)
{
OutputNode element = node.getNode();
Class javaType = type.getType();
if (ObjectBase.class.isAssignableFrom(javaType))
{
_logger.Verbose("Old name was " + element.getName());
element.setName(javaType.getSimpleName());
_logger.Verbose("Changing name to " + javaType.getSimpleName());
}
try
{
String className = element.getAttributes().get("class").getValue();
Class localClass = Class.forName(className);
boolean shouldShortenName = ObjectBase.class.isAssignableFrom(localClass);
if (shouldShortenName)
{
element.getAttributes().remove("class");
element.setName(localClass.getSimpleName());
}
}
catch (Exception e)
{
e.printStackTrace();
}
if (ObjectBase.class.isAssignableFrom(javaType))
{
element.setName(type.getType().getSimpleName());
}
}
}

Java Preferences and Internationalization (i18n)

The Java tutorials recommend using the Preferences API over Properties files.
Properties files and ResourceBundles are the recommended way to handle Internalization requirements in applications.
I am considering using both for a desktop application that will display preferences in a locale specific way.
Can anyone point out problems with this approach?
Maybe I should just use Properties files period?
I am considering using both for a desktop application that will display preferences in a locale specific way.
OK, so what you want is translated configuration file in form of:
some_translated_key=some_value
Well, unless you want to support MUI at some point it should not be a big deal. However, if you do, so that different users on the same computer could use different languages, or user might be able to switch language, you would have troubles in matching key to a property. You would have to scan all translations while reading the key, and you would surely end up with multiple entries for the same key. How to resolve that? Well, that's a good question.
From my experience, configuration files should be language-independent (neutral culture) and should never be edited by hand (that is translating keys doesn't really matter).
I thought there could be a problem with character encoding, but following code snippet works without an issue (files are UTF-8 encoded):
public class Main {
private static final String FILE_NAME = "i18ned.properties";
private File propertiesFile;
private Properties properties;
public Main() {
properties = new Properties();
propertiesFile = new File(FILE_NAME);
if (propertiesFile.exists()) {
try {
properties.load(new BufferedReader(new FileReader(
propertiesFile)));
} catch (FileNotFoundException e) {
// not likely, but should be logged either way
} catch (IOException e) {
// logger should be used instead
e.printStackTrace();
}
}
}
public void saveProperties() {
try {
properties
.store(new BufferedWriter(new FileWriter(propertiesFile)), "");
} catch (IOException e) {
// oops, use logger instead
e.printStackTrace();
}
}
public static void main(String[] args) {
Main main = new Main();
main.storeSome();
main.readSome();
}
private void readSome() {
String highAsciiKey = "żółć";
String value = properties.getProperty(highAsciiKey);
System.out.println(value);
}
private void storeSome() {
String highAsciiKey = "żółć";
String highAsciiValue = "łąkę";
properties.setProperty(highAsciiKey, highAsciiValue);
saveProperties();
}
}
Using resource bundle for localizing applications is the standard way in java. The problems of this way are:
there is no compile time check of number and type of parameters required by resource.
It is hard to hold files clean, e.g. there is no mechanism the helps to remove unused strings
It is hard to make all texts translated to all supported languages.
etc....
The probably better internationalization mechanism is suggested by Google in their GWT. They generate class with method per string.
For example if you have text Hello, {0} they will generate method
String hello(String name);
So, you cannot pass neither 0 nor 2 arguments to this method. Only one.
This partially solves the second problem also. It is easier to see if method is not used in whole project. It does not solve the 3rd problem anyway.

Categories

Resources