I am getting started with StringTemplate 4 and I am trying to create a template from a simple string stored in a database. I use something like this:
STGroup group = new STGroupString(null, someTemplateString, '$', '$');
ST st = group.getInstanceOf(someTemplateName);
st.add(someAttribute, someValue);
Now everything works fine if I define all or less than the attribute defined for the template someTemplateName. Now if I try to add an attribute that doesn't exist, I get the following exception:
no such attribute: fake
java.lang.IllegalArgumentException: no such attribute: fake
...
which makes sense. However, it seems like there's no way for me to know beforehand which attributes are defined for the template someTemplateName. I was expecting to find something like:
bool isDef = st.isDefined(someAttribute);
but there's no such method. Am I correct? Is there any way around this?
The documentation for CompiledST states tokens is only for debug. Not sure what that means.
ST template = new ST("Hello <username>, how are you? Using <if(condition)>expression<endif> in condition works, and repeating <username> is not a problem.");
Set<String> expressions = new HashSet<String>();
TokenStream tokens = template.impl.tokens;
for (int i = 0; i < tokens.range(); i++) {
Token token = tokens.get(i);
if (token.getType() == STLexer.ID) {
expressions.add(token.getText());
}
}
Gives you the Strings username and condition.
You can use st.impl.formalArguments to access the Map<String, FormalArgument> where the arguments are defined. Note that for some templates this field will be null.
Related
I have a field that was created based on some parsed and reformatted data. If the final result does not match a specific pattern, I want to replace it with just a blank value. I have some groovy code written but I continue to get errors of "No such property" when I run it.
I have tried poking around online and moving the code around but with my very limited experience I am not having any luck.
def formattedData=data['FormattedNum']
def regExpStr=[A-Z]{5}[':'][0-9]{4}
if (formattedData.matches(regExpStr)){
formattedData
} else {
formattedData =''
} ;
My expected results would be to retain the data['FormattedNum'] if it matches the regex and if it doesn't, just replace whatever is in that field as a blank.
What is data? Is it a map?
Lets assume it is and it's out of scope in the method or wherever this code is located...
def data = [FormattedNum: 'ABCDE:0123']
def formattedData= data['FormattedNum']
def regExpStr = /[A-Z]{5}[':'][0-9]{4}/
if (formattedData.matches(regExpStr)){
formattedData
}
else {
formattedData = ''
}
You can do this:
def formattedData = data['FormattedNum']
if (!(formattedData ==~ '[A-Z]{5}:[0-9]{4}')) {
formattedData = ''
}
==~ matches the subject with the regular expression as explained in the official Groovy doc. It has the same semantics as calling matches on a string but in groovy way
Does anybody know if there is any easy way within Java to prefix one string onto multiple other strings?
For example, if I have the following snippet of Java code ;
String includeDir = "/usr/local/boost-1.52.0/include";
ArrayList<String> filenamesRelative = new ArrayList<String>(),
filenamesAbsolute = new ArrayList<String>();
filenamesRelative.add("/boost/aligned_storage.hpp");
filenamesRelative.add("/boost/any.hpp");
I would like to be able to prefix the value of the variable 'includeDir', i.e. "/usr/local/boost-1.52.0/include", onto the front of each value in the ArrayList filenamesRelative.
Ideally, I would like to be able to do something like the following ;
filenameAbsolute = filenamesRelative.prefixAll(includeDir);
I don't necessarily have to use ArrayLists in the solution; I have just used them above for illustrative purposes.
From memory, you can do something like this rather easily in C++ using the STL, however my current working knowledge of Java isn't all that good unfortunately :(
Thanks in advance for any assistance.
I dont know of a method in the API. but its so simple just create your own. Something like:
List<String> filenameAbsolute = new ArrayList<String>();
for ( String file: filenamesRelative ) {
filenameAbsolute.add(prefix + file);
}
Do it like this:
ArrayList<String> prefixAll(ArrayList<String> filenamesRelative)
{
ArrayList<String> filenamesAbsolute = new ArrayList<String>();
String includeDir = "/usr/local/boost-1.52.0/include";
for ( String file: filenamesRelative ) {
filenameAbsolute.add(includeDir + file);
}//for
return filenameAbsolute;
}//prefixAll()
I am looking for some nice solution. I've got a couple of textfields on my page and I am sending these via Ajax using jQuery serialize method. This serialized string is parsed in my java method to hashmap with key = 'nameOfTextfield' nad value = 'valueInTextfield'
For example, I've got this String stdSel=value1&stdNamText=value2&stdRevText=value3 and everything works fine.
String[] sForm = serializedForm.split("&");
Map<String, String> fForm = new HashMap<String, String>();
for (String part : sForm) {
String key = null;
String value = null;
try {
key = part.split("=")[0];
value = part.split("=",2)[1];
fForm.put(key, value);
//if textfield is empty
} catch(IndexOutOfBoundsException e) {
fForm.put(key, "");
}
}
But this method will break down when ampersand in some textfield appears, for example this stdSel=value1&stdNamText=value2&stdRevText=val&&ue3. My thought was that I'll replace ampersand as separator in searialized string for some other character or maybe more characters. Is it possible and good idea or is there any better way?
Regards
Ondrej
Ampersands are escaped by the serialize function, so they don't break the URL.
What you need to unescape a field you got from an URL is
value = URLDecoder.decode(value,"UTF-8");
But, as was pointed by... Pointy, if you're using a web framework and not using only vanilla java.net java you probably don't have to do this.
I have the following code that defines a getParts method to find a given Part Name and Part Number in the system. Note that this code comes from our system's API, so if no one can help I'll just delete this question. I figured someone could potentially see a solution or help me along the way.
<%! private QueryResult getParts( String name, String number )
throws WTException, WTPropertyVetoException {
Class cname = wt.part.WTPart.class;
QuerySpec qs = new QuerySpec(cname);
QueryResult qr = null;
qs.appendWhere
(new SearchCondition(cname,
"master>name",
SearchCondition.EQUAL,
name,
false));
qs.appendAnd();
qs.appendWhere
(new SearchCondition(cname,
"master>number",
SearchCondition.EQUAL,
number,
false));
qr = PersistenceHelper.manager.find(qs);
System.out.println("...found: " + qr.size());
return qr;
}
%>
But I would like to allow the user more flexibility in finding these parts. So I set up conditional statements to check for a radio button. This allows them to search by part name and part number, find all, or search using a wildcard. However, I'm having trouble implementing the two latter options.
To attempt to accomplish the above, I have written the below code:
<%
String partName = request.getParameter("nameInput");
String partNumber = request.getParameter("numberInput");
String searchMethod = request.getParameter("selection");
//out.print(searchMethod);
QueryResult myResult = new QueryResult();
if(searchMethod.equals("search"))
myResult = getParts(partName, partNumber);
else if(searchMethod.equals("all"))
{
//Should I write a new function and do this?
//myResult = getAllParts();
//or is there a way I could use a for each loop to accomplish this?
}
//else if(searchMethod.equals("wildcard"))
//get parts matching %wildcard%
while(myResult.hasMoreElements())
{
out.print(myResult.nextElement().toString());
}
%>
Basically, it accepts user input and checks what type of search they would like to perform. Is there an easy way to pass all the values into the myResult object? And likewise for the wildcard search? Like I said before, it may be futile trying to help without access to the API, but hopefully it isn't.
Thanks!
You can (and should) reuse the function, but in order to do so, you will need a part name and number (as those are its input parameters). So for the multi-result options you will need to get a list/collection of part names+numbers and feed them individually to the function, then collect the result in the format that is most appropriate for your needs
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.