I have a method called render_something which can creates a lot of whitespace, for example:
#render_something('xxx')
The result can be:
<a href="#">
something that generate from redner_something
</a>
Which actually I want it to be like this:
something that generate from redner_something
Does velocity has something like this?
#trim(#render_something('xxx'))
I just read this article on Velocity Whitespace Gobbling which suggests a few work-arounds including Velocity Whitespace Truncated By Line Comment.
This basically suggests commenting out line breaks by putting comments at the end of each line. It also suggests not indenting the code in your macros to prevent superfluous (one of my favourite words) spaces occurring.
TBH it's not a great solution but may suit your needs. Simply put ## at the end of each line in your macro and that will make things a little bit nicer... sort of
It seems just java native trim() works.
$someValue.trim() works for me
Solution
In the class where you create the VelocityEngine, add a method as follows
public String trim(String str) {
return str.trim()/*.replace("\n", "").replace("\r", "")*/;
}
then add the following to the VelocityContext that you create:
context.put("trimmer", this);
and finally in the velocity template do the following
$trimmer.trim("#render_something('xxx')")
Why does it work?
Although the behavior of Velocity is clearly define, it can be a bit tricky to see how it works sometimes. The separate trim()-method is necessary to get the char-sequence from the template into a Java method where you can call the actual trim() on the String. As far as I know there is no trim inside Velocity, but you always can call back to Java with tricks like this one.
The double-quotes are necessary because the #render_something is just a macro, not a function call, this means the results of the statements in the macro are put verbatim into the point where the macro is "executed".
I struggled a while to find a straightforward solution to whitespace gobbling, so here the one I finally came up with. It is inspired from and Vadzim's answer and this page http://wiki.apache.org/velocity/StructuredGlobbingResourceLoader
The StructuredGlobbingResourceLoader we can find on the website has a complex behaviour and doesn’t get rid of any kind of whitespace, so I modified it to get the simple behaviour: "Delete any whitespace at the beginning of the lines, and add a comment at the end of each line" (which prevents the linebreak evaluation). The filter is applied on the input stream at loading time.
This kind of velocity template
#if($value)
the value is $value
#end
is transformed to
#if($value)##
the value is $value##
#end##
Then if you want to have linebreaks or beginning of line whitespaces, you'll have to put($br,"\n") and put($sp," ") in your context like Vadzim's explained and explicitly use them in your template. This way of doing will allow you to keep indented templates, with maximum control.
take the class from this page http://wiki.apache.org/velocity/StructuredGlobbingResourceLoader
change the extended class to the kind of loader your need (this one uses the webapp loader)
replace the read() method with the code I provide
use the class as your resource loader in your properties. Example for the webapp loader: webapp.resource.loader.class=...StructuredGlobbingResourceLoader
public int read() throws IOException {
int ch;
switch(state){
case bol: //beginning of line, read until non-indentation character
while(true){
ch = in.read();
if (ch!=(int)' ' && ch!=(int)'\t'){
state = State.content;
return processChar(ch);
}
}
case content:
ch = in.read();
return processChar(ch);
//eol states replace all "\n" by "##\n"
case eol1:
state = State.eol2;
return (int)'#';
case eol2:
state = State.bol;
return (int)'\n';
case eof:
return -1;
}
return -1;
}
//Return the normal character if not end of file or \n
private int processChar(int ch){
switch(ch){
case -1:
state = State.eof;
return -1;
case (int)'\n':
state = State.eol1;
return (int)'#';
default:
return ch;
}
}
Any feedback on my implementation is welcome
Inspired by Velocity Whitespace Truncated By Line Comment one could use block comments instead of line comments for a better looking result:
#foreach( $record in $records )#**
*##if( $record.id == 0 )#**
*##end
#end
With a decent syntax highlighting the comments aren't very obtrusive.
Here is my alternative solution to velocity whitespace gobbling that allows tabbing template structure.
Each template text is preprocessed on first load in custom ResourceLoader:
private String enhanceTemplate(String body) {
if (!body.startsWith("##preserveWhitespace")) {
body = body.replaceAll("(##.*)?[ \\t\\r]*\\n+[ \\t\\r]*", Matcher.quoteReplacement("##\n"));
body = body.trim();
}
return body;
}
This replaces all new lines and adjustent spaces with just one commented newline.
Line breaks and tailing spaces can be inserted explicitly with $br and $sp variables from default context:
private static final VelocityContext DEFAULT_CONTEXT = new VelocityContext(new HashMap<String, String>() {{
put("sp", " ");
put("br", "\n");
}});
In some cases, I've had to essentially minimize my script like I would js or css. It works well, though it is not as easy for humans to read. Just one other option to eliminate the excess space:
<ul class="tabs">#foreach($par in $bodypars)#set( $parLen = ${_MathTool.toInteger($bodypars.size())} )#set( $parLn = $parLen - 1 )#set( $thClass = 'tb'+${parLn} )#set( $thaClass = '' )#if( $foreach.index == 1 )#set( $thClass = ${thClass}+' selected' )#set( $thaClass = ' selected' )#end#if($foreach.index != 0 && $parLen <= $maxTabs)#set ( $btitle = $_XPathTool.selectSingleNode($par,'item-subtitle') )<li class="${thClass}">#if($!btitle && $btitle != '')$_SerializerTool.serialize($btitle, true)#end</li>#end#end</ul>
You can use standard java trim, taking attention to your variable if are a object instead string.
$string.trim() //work fine
$object.trim() //exception
Have a good day!
Related
Let's say we have a java file that looks like this :
class Something {
public static void main(String[] args){
System.out.println("Hello World!");
}
}
I would like to write some Kotlin code that would go through this java file and detect how many lines there is in the method body (Here is the main method only). Empty lines are counted!
My approach is to simply use the File forEachline method to read the java file line by line. I can write code to detect the method signature. Now I want to be able to determine where the method ends. I don't know how to do that.
If I simply look for "}" my code could make mistakes assuming that we are at the end of the body of the method while in reality we are at the end of an if statement body within the method.
How can I avoid this pitfall?
One way to approach this is keeping track of the number of open brackets('{') and close brackets ('}') seen. At the start of the method, the count will increment to 1. Assuming the method is validly structured, at the end of the method the number of unclosed brackets should be 0. Pseudocode like this should work:
int numLines = 1 (assuming method start line counts)
int numBrackets = 1 (after finding method open bracket for method)
while(numBrackets > 0)
if char = '{' -> numBrackets++
if char = '}' -> numBrackets--
if char = newline -> numLines++
if numBrackets not 0 -> FAIL
return numLines
Edit
As noted by Gidds below, this pseudo-code is insufficient. A more complete answer will need to include the fact that not all brackets impact method structure. One way to approach this is by keeping track of the context of the current character being parsed. Only increment/decrement numBrackets when in a valid context (non-string literal, comment, etc..). Though as noted by Gidds, this will increase complexity. Updated Pseudocode:
int numLines = 1
int numValidBrackets = 1
Context context = Context(MethodStructure)
while(numValidBrackets > 0)
context.acceptNextChar(char)
if char = newline -> numLines++
if(context.state() != MethodStructure) continue;
if char = '{' -> numValidBrackets++
if char = '}' -> numValidBrackets--
if numBrackets not 0 -> FAIL
return numLines
I want to compare a string portion (i.e. character) against a Chinese character. I assume due to the Unicode encoding it counts as two characters, so I'm looping through the string with increments of two. Now I ran into a roadblock where I'm trying to detect the '兒' character, but equals() doesn't match it, so what am I missing ? This is the code snippet:
for (int CharIndex = 0; CharIndex < tmpChar.length(); CharIndex=CharIndex+2) {
// Account for 'r' like in dianr/huir
if (tmpChar.substring(CharIndex,CharIndex+2).equals("兒")) {
Also, feel free to suggest a more elegant way to parse this ...
[UPDATE] Some pics from the debugger, showing that it doesn't match, even though it should. I pasted the Chinese character from the spreadsheet I use as input, so I don't think it's a copy and paste issue (unless the unicode gets lost along the way)
oh, dang, apparently it does not work simply copy and pasting:
Use CharSequence.codePoints(), which returns a stream of the codepoints, rather than having to deal with chars:
tmpChar.codePoints().forEach(c -> {
if (c == '兒') {
// ...
}
});
(Of course, you could have used tmpChar.codePoints().filter(c -> c == '兒').forEach(c -> { /* ... */ })).
Either characters, accepting 兒 as substring.
String s = ...;
if (s.contains("兒")) { ... }
int position = s.indexOf("兒");
if (position != -1) {
int position2 = position + "兒".length();
s = s.substring(0, position) + "*" + s.substring(position2);
}
if (s.startsWith("兒", i)) {
// At position i there is a 兒.
}
Or code points where it would be one code point. As that is not really easier, variable substring seem fine.
if (tmpChar.substring(CharIndex,CharIndex+2).equals("兒")) {
Is your problem. 兒 is only one UTF-16 character. Many Chinese characters can be represented in UTF-16 in one code unit; Java uses UTF-16. However, other characters are two code units.
There are a variety of APIs on the String class for coping.
As offered in another answer, obtaining the IntStream from codepoints allows you to get a 32-bit code point for each character. You can compare that to the code point value for the character you are looking for.
Or, you can use the ICU4J library with a richer set of facilities for all of this.
I am reading a pl/sql code from a text file and storing all words of it into array list from below code :
Scanner in1 = new Scanner(file1);
ArrayList<String> Code1 = new ArrayList<String>();
in1.useDelimiter("/\\*[^*]*\\*+(?:[^/*][^*]*\\*+)*/|[\\p{javaWhitespace}\\.|,]+");
while (in1.hasNext())
{
Code1.add(in1.next().toLowerCase());
}
Everything is working fine but i am facing issue whenever there is a comments section in code written in after special character -- Like below:
select * from
Dummy_Table --This is a, dummy.table
where id = 1 -- Filter.on, id
For above code i don't want to store the comments (--This is a, dummy.table) and (-- Filter.on, id) in my list.How can i do this ?
in1.useDelimiter("/\\*[^*]*\\*+(?:[^/*][^*]*\\*+)*/|[\\p{javaWhitespace}\\.|,]+");
I am using above delimiter to skip reading comment section enclosed between /* and */, which is multiple line comments as written below but including this i also want to skip reading/storing the single statement comments i.e. after -- till the end of line.
/*
|| This is a comments section in pl/sql code...||
|| Which i don't want to store.. ||
*/
You can add part after -- till end of line in your regex like this:
in1.useDelimiter("/\\*[^*]*\\*+(?:[^/*][^*]*\\*+)*/|--[^\\n]*|[\\p{javaWhitespace}\\.|,]+");
RegEx Demo
For removing inline comments, have you considered using the simple combination of indexOf() and substring()? Also, should you be using in1.nextLine() instead of in1.next()? For example:
while (in1.hasNext())
{
String line = in1.nextLine();
int indexOfComment = line.indexOf("--");
if (indexOfComment > -1) {
line = line.substring(0, indexOfComment);
}
Code1.add(line.toLowerCase());
}
I have a situation in a free-text file, where between any pair of two string matches of my choice - e.g.
<hello> and </hello>
I want to replace the occurrence of a third string-match with a different string e.g. '=' with '&EQ;'
e.g.
hi=I want this equals sign to stay the same,but=<hello>
<I want="this one in the hello tag to be replaced"/>
</hello>,and=of course this one outside the tag to stay the same
becomes
hi=I want this equals sign to stay the same,but=<hello>
<I want&EQ;"this one in the hello tag to be replaced"/>
</hello>,and=of course this one outside the tag to stay the same
Basically this is because an XML body is being sent in a value-pair and it is royally screwing things up (I am sent this format by a venue and don't have control over it
My immediate approach was to start with a BufferedReader and parse into a StringBuilder going through line by line using String.indexOf( ) to toggle on and off whether we are in tags or not, but 20 minutes in to this approach it occurred to me this may be a bit brute-force and there might be an existing solution to this kind of problem
I know this approach will work eventually but my question is, is there a better way (that is one that is higher level and uses existing Java libraries / common frameworks e.g. Apache Commons, etc. which would make it less error-prone and more maintainable. I.e. is there a more intelligent way of solving this problem than the approach I am taking? Which is effectively brute-force parsing.
If you want to escape XML, have a look at Apache Commons Lang StringEscapeUtils, specifically StringEscapeUtils.escapeXML, it should do what you need.
My great impenetrable solution is as follows, and it seems to work.
I do apologise that it's so hard to follow but it basically came down to this from factorising and re-factorising, many times over combining similar pieces of code.
It will replace all the occurences of String 'replace' with String 'with' between tokens of 'openToken' and 'closeToken' and should be started with mode=false to begin with
As with most things in life, there's probably a really clever succinct way to do this with RegEx
boolean mode=false
StringBuilder output
while( String line = newLine ) {
mode = bodge( "<hello>", "</hello>", "=", "&EQ;", output, mode );
}
private static boolean bodge( String openToken, String closeToken, String replace, String with, String line, StringBuilder out, boolean mode ) {
String comparator = mode ? closeToken : openToken;
int index = line.indexOf( comparator );
// drop through straight if nothing interesting
if( index == -1 ) {
String outLine = mode ?
replacer( line , replace, with ) :
line;
out.append( outLine );
out.append( "\r\n" );
return mode;
}
else {
int endOfToken = index + comparator.length();
String outLine = line.substring(0, endOfToken);
outLine = mode ?
replacer( outLine , replace, with ) :
outLine;
out.append(outLine );
return bodge( openToken, closeToken, replace, with, line.substring( endOfToken ), out, !mode );
}
}
I have a textfield called x.
When the textfield contains " ", I want to do something. If it does not, do something else.
I tried doing
String test = x.getText();
if(test.startsWith(" ")){buttonN.setForeground(Color.GRAY));}
else{buttonN.setForeground(Color.BLACK));}
but it didnt work. any suggestions
Why not use contains?:
if(x.getText().contains("\u0020"))
buttonN.setForeground(Color.GRAY);
else
buttonN.setForeground(Color.BLACK);
Although the aforementioned will work, it won't detect tabular spacing. That being said, I'd recommend using a regular expression instead:
if(Pattern.compile("\\s").matcher(x.getText()).find())
buttonN.setForeground(Color.GRAY);
else
buttonN.setForeground(Color.BLACK);
Reference.
If you just want to ensure if the text field is empty regardless of whether it contains space, tab, newline etc. use the following:
if(x.getText().trim().length() == 0){
buttonN.setForeground(Color.GRAY);
}else{
buttonN.setForeground(Color.BLACK);
}
The String.trim() removes any whitespace in the String.
The easiest solution for any verifycation of the getText() command is this:
If (field.getText().isEmpty()) {
buttonN.setForeground(Color.GRAY);
}
else {
buttonN.setForeground(Color.BLACK);
}
(Color.GRAY)) and (Color.BLACK)) end with 2 closing parenthesis, while only one was opened.
String test = x.getText();
if (test.startsWith (" "))
{
buttonN.setForeground (Color.GRAY);
}
else buttonN.setForeground (Color.BLACK);
Some spaces around parenthesis make the reading more convenient.