Context
I'm making some changes to a grammar parser in ANTLR3 to recognize certain URLs that I consider valid (for example, if it begins with www or http, or if it ends with .com)
The main rule I have right now is something like this:
url returns [UrlToken token]
: scheme_full? subdomain? middle_url valid_domain_end? port? path_full?
{isValidUrl($scheme_full.text, $subdomain.text, $valid_domain_end.text, $path_full.text, $middle_url.text)}?
{$token = urlTokenFor(
StringUtils.defaultString($scheme_full.text),
StringUtils.defaultString($subdomain.text) + StringUtils.defaultString($middle_url.text) + StringUtils.defaultString($valid_domain_end.text),
StringUtils.defaultString($path_full.text),
StringUtils.defaultString($port.text),
StringUtils.defaultString(null) ); }
;
And the code of isValidUrl is:
private boolean isValidUrl(String scheme, String subdomain, String valid_end, String path, String middle) {
if (scheme != null || subdomain != null || valid_end != null) return true;
if (path != null && middle.contains(".")) return true;
return false;
}
The main idea is that I let the sub-parts match optionally, then check if it's a valid URL using some Java function, and then (in case it is valid) it returns a UrlToken.
I did it like this because the logic to check if a URL is valid was too complicated to leave it to the parser, generated a lot of ambiguity which I wasn't able to fix.
Now, this is currently working fine for me, and is matching the URLs that I want.
The problem is that before the changes I was using the memoize option, and I had to deactivate it to make it work. Since performance is important I'd like to be able to keep memoization enabled.
Why is it failing when using memoize?
I did some debugging on the generated Java code.
This is a piece of the generated code for the url rule:
// grammars/Url.g:94:5: ( scheme_full )?
int alt3=2;
int LA3_0 = input.LA(1);
if ( (LA3_0==LETTERS) ) {
int LA3_1 = input.LA(2);
if ( (LA3_1==SYMBOL) ) {
int LA3_3 = input.LA(3);
if ( (LA3_3==SYMBOL) ) {
int LA3_4 = input.LA(4);
// Inside synpred3_Url(), scheme_full() is executed
if ( ((synpred3_Url()&&(matchesScheme(input.LT(1))))) ) {
alt3=1;
}
}
}
}
switch (alt3) {
case 1 :
// grammars/Url.g:0:0: scheme_full
{
pushFollow(FOLLOW_scheme_full_in_url100);
scheme_full2=scheme_full();
state._fsp--;
if (state.failed) return retval;
}
break;
}
And this is the generated code for the scheme_full as an example:
public final Url.scheme_full_return scheme_full() throws RecognitionException {
Url.scheme_full_return retval = new Url.scheme_full_return();
retval.start = input.LT(1);
int scheme_full_StartIndex = input.index();
try {
if ( state.backtracking>0 && alreadyParsedRule(input, 13) ) { return retval; }
// grammars/Url.g:149:3: ( scheme scheme_separator )
// grammars/Url.g:149:5: scheme scheme_separator
{
pushFollow(FOLLOW_scheme_in_scheme_full374);
scheme();
state._fsp--;
if (state.failed) return retval;
pushFollow(FOLLOW_scheme_separator_in_scheme_full376);
scheme_separator();
state._fsp--;
if (state.failed) return retval;
}
retval.stop = input.LT(-1);
}
catch (RecognitionException re) {
reportError(re);
recover(input,re);
}
finally {
if ( state.backtracking>0 ) { memoize(input, 13, scheme_full_StartIndex); }
}
return retval;
}
This are the conclusions I've come to so far:
Each rule becomes a Java function, each one is executed twice,
the first one in the form of a backtracking to "predict" if the term will be used,
and the second one when it's actually matching.
The return values of the terms such as scheme_full, subdomain, etc. are of the type ParserRuleReturnScope, which contains start and stop tokens.
When memoize is set to true, the first time the terms are executed if the result is successful, it is cached, so that the second time it's gonna execute it doesn't need to do all over again. When the first time is successful (before putting the result in cache) the result (retval) has both start and end tokens properly set.
The problem is that the second time it is executed, and uses the cached result, the stop token comes in null (although the first execution was successful). It can be seen in the scheme_full code, when retval is returned it doesn't have the stop token set yet.
This last thing becomes a problem because when executing the predicate (the isValidUrl) it uses the text attribute of the term, using $scheme_full.text for example. And because the end token is in null, calling the text attribute to the scope returns a null string, and so my validation fails, even though the match was successful.
This whole behavior doesn't happen when memoize is set to false.
Any idea how can I solve this?
Related
I want to replace nested for loops in the following code with streams:
private boolean check(St st) {
List<Co> prereqs = getCoPrereqs();
for (Co prereq : prereqs) {
List<En> stEns = st.getEns();
boolean flag = false;
for (En en : stEns) {
if (en.getCo().equals(prereq) && en.getGr() != null) {
if (en.hasPassedCo()) {
flag = true;
}
}
if (!flag)
return false;
}
}
return true;
}
The two loops and the variable flag is causing confusion. I am not sure if this can be converted to streams totally.
I have simplified your code somewhat by doing the following:
removing the boolean flag. It isn't necessary.
get the List<En> just one time outside of the Prereq loop. You can reiterate the original as often as necessary.
The major difference is to check for a false return from en.hasPassedCo() and return false immediately. Once the iterations are complete, then return true.
private boolean check(St st) {
List<Co> prereqs = getCoPrereqs();
List<En> stEns = st.getEns();
for (Co prereq : prereqs) {
for (En en : stEns) {
if (en.getCo().equals(prereq) && en.getGr() != null) {
if (!en.hasPassedCo()) {
return false;
}
}
}
}
return true;
}
I'm not certain that streams would improve this (at least not knowing more about the relationships of the fields to each other). Also, it doesn't make sense how Co relates to en.getCo. Seems to me that something like prereqs.contains(en.getCo()) would be more appropriate.
Probably, you can use nested streams with allMatch.
I'm saying "probably" because I can't be sure that the code you've proved does what expected, types name are not self-explanatory at all (names in the code matter a lot) and you have not accompanied the code with any explanations.
If I understood your code correctly, you need to validate every Co object returned by getCoPrereqs() and that entails checking each Co object against En object from a List<En> which should be extracted from the method parameter.
That's how it might look like:
private boolean check(St st){
return getCoPrereqs().stream()
.allMatch((Co prereq) -> st.getEns().stream()
.allMatch((En en) -> en.getCo().equals(prereq)
&& en.getGr() != null
&& en.hasPassedCo()
));
}
For readability reasons (to make it more easier to compare stream with loops), I've used explicitly typed lambda expressions (the common practice is to omit types for brevity and let the type inference do the job).
I have a function that is supposed to return a place object, but I also need to test on whether something evaluates to false, and in addition the caller needs to know both of those pieces of information. I have the return type as Place but in Java there are no reference parameters, so if the following if-condition is true, I would like for some way to reflect that in the caller so I can check it, but I can't have more than one return type so I'm stuck as to what to do. My best shot was returning null but I just get the feeling that this is bad programming.
if ( directions.get(i).isLocked() )
Below is the complete function:
Place followDirection(String dir, boolean isLocked) {
dir = dir.toLowerCase(); // make sure the string is lowercase for comparisons
int i = 0;
for ( i = 0; i < directions.size(); i++ ) { // loop until we find a match, remember that if it's locked then we cnanot go in there
if ( directions.get(i).getDirection().equals(dir) ) {
if ( directions.get(i).isLocked() ) {
System.out.println("This room is locked, sorry");
}
else {
return directions.get(i).getToPlace(); // this means we found a match, return the destination
}
}
}
Place p = null;
return p;
}
Technically, there are two options if you don't want to return null (which does not seem bad by the way):
return an object that contains both return values
Pass in a mutable object as parameter.
The second option also feels somewhat dirty.
java is a call by value language but it is a little bit complicated. this language pass the pointers as a value and if you dont change the pointer you can change the object that pass to your function. for example if you pass an complex object to a function and in that function you change the value of a parameter of that object, the caller can see it, in your code you can pass an object than contains dir and isLocked , so you can change those parameters.
Place followDirection(MyObject obj) {
obj.dir = obj.dir.toLowerCase(); // make sure the string is lowercase for comparisons
int i = 0;
for ( i = 0; i < directions.size(); i++ ) { // loop until we find a match, remember that if it's locked then we cnanot go in there
if ( directions.get(i).getDirection().equals(obj.dir) ) {
if ( directions.get(i).isLocked() ) {
System.out.println("This room is locked, sorry");
}
else {
return directions.get(i).getToPlace(); // this means we found a match, return the destination
}
}
}
Place p = null;
return p;
}
MyObject contains :
String dir, boolean isLocked
I'm trying to make a group of if statements, in which each if will print given some argument is true, but an else that will only print if none of the ifs were returned. I don't think an else if would work in this case.
I have some code (the colors are just as examples):
boolean any=false;
if(redStage==2)
{ any=true; System.out.print(redComplete); }
if(blueStage==2)
{ any=true; System.out.print(blueComplete); }
if(greenStage==2)
{ any=true; System.out.print(greenComplete); }
if(any==false)
System.out.print(noneComplete);
Is there anything I can do to eliminate the need for a separate boolean to check whether any of the if's arguments were true?
Edit:
(I just noticed what may be confusing. The code im using isn't actually using return. Instead, it is printing out the results, which means more than one thing can be returned.)
Since you need to processes the stages independently from one another, and more than one can be complete at the same time, your code is as good as it can be.
What follows is my answer to your original question:
You don't need the boolean. Your code is equivalent to:
if (redStage == 2) { return redComplete; }
if (blueStage == 2) { return blueComplete; }
if (greenStage == 2) { return greenComplete; }
return noneComplete;
This makes use of the fact that each if body contains an unconditional return. If this wasn't the case, you could phrase the construct like so:
if (redStage == 2) {
// redComplete
} else if (blueStage == 2) {
// blueComplete
} else if (greenStage == 2) {
// greenComplete
} else {
// noneComplete
}
I am working on a program that gives the user the opportunity to define his own selection string. For example in the expression:
doc.select("a[href]");
The user would have specified the "a[href]" part. Now my question is, how can I check if the passed in string is a valid JSoup selector string? Does anyone know how to validate this?
Any help would be very much appreciated.
Thanks
approach 1: Edit the Jsoup source and make the Parser public or implement your own method there
approach 2: Parse a simple dummy element and catch the exceptions. if one is thrown: query is not valid, else it's ok. Not the best solution but it works.
Here's an example:
private static final Element dummy = new Element(Tag.valueOf("p"), ""); // used for "testparsing"
// ...
public static boolean isValid(String query)
{
if( query == null ) // Check for null
return false;
try
{
Selector.select(query, dummy); // Use the querystring on the dummy - returnvalue is not relevant
}
catch( Selector.SelectorParseException | IllegalArgumentException ex ) // these exceptions are thrown if something is not ok
{
return false; // If something is not ok, the query is invalid
}
return true; // All ok, query is valid
}
Test:
System.out.println(isValid(null)); // not valid
System.out.println(isValid("div.abc")); // valid
System.out.println(isValid("p[")); // not valid
System.out.println(isValid("a:matchesxy")); // not valid
System.out.println(isValid("div > a")); // valid
Testresult:
false
true
false
false
true
Check for jsoup sources : you can use the Selector. Selector is using QueryParser which is not public, but you can "check" it too.
using QueryParser will be more reasonable
try {
QueryParser.parse(contentSelector);
return true;
} catch( Selector.SelectorParseException ex ) {
return false;
}
Greetings,
I'm trying to validate whether my integer is null. If it is, I need to prompt the user to enter a value. My background is Perl, so my first attempt looks like this:
int startIn = Integer.parseInt (startField.getText());
if (startIn) {
JOptionPane.showMessageDialog(null,
"You must enter a number between 0-16.","Input Error",
JOptionPane.ERROR_MESSAGE);
}
This does not work, since Java is expecting boolean logic.
In Perl, I can use "exists" to check whether hash/array elements contain data with:
#items = ("one", "two", "three");
##items = ();
if (exists($items[0])) {
print "Something in \#items.\n";
}
else {
print "Nothing in \#items!\n";
}
Is there a way to this in Java? Thank you for your help!
Jeremiah
P.S. Perl exists info.
parseInt() is just going to throw an exception if the parsing can't complete successfully. You can instead use Integers, the corresponding object type, which makes things a little bit cleaner. So you probably want something closer to:
Integer s = null;
try {
s = Integer.valueOf(startField.getText());
}
catch (NumberFormatException e) {
// ...
}
if (s != null) { ... }
Beware if you do decide to use parseInt()! parseInt() doesn't support good internationalization, so you have to jump through even more hoops:
try {
NumberFormat nf = NumberFormat.getIntegerInstance(locale);
nf.setParseIntegerOnly(true);
nf.setMaximumIntegerDigits(9); // Or whatever you'd like to max out at.
// Start parsing from the beginning.
ParsePosition p = new ParsePosition(0);
int val = format.parse(str, p).intValue();
if (p.getIndex() != str.length()) {
// There's some stuff after all the digits are done being processed.
}
// Work with the processed value here.
} catch (java.text.ParseFormatException exc) {
// Something blew up in the parsing.
}
Try this:
Integer startIn = null;
try {
startIn = Integer.valueOf(startField.getText());
} catch (NumberFormatException e) {
.
.
.
}
if (startIn == null) {
// Prompt for value...
}
ints are value types; they can never be null. Instead, if the parsing failed, parseInt will throw a NumberFormatException that you need to catch.
There is no exists for a SCALAR in Perl, anyway. The Perl way is
defined( $x )
and the equivalent Java is
anInteger != null
Those are the equivalents.
exists $hash{key}
Is like the Java
map.containsKey( "key" )
From your example, I think you're looking for
if ( startIn != null ) { ...
For me just using the Integer.toString() method works for me just fine. You can convert it over if you just want to very if it is null. Example below:
private void setCarColor(int redIn, int blueIn, int greenIn)
{
//Integer s = null;
if (Integer.toString(redIn) == null || Integer.toString(blueIn) == null || Integer.toString(greenIn) == null )
I don't think you can use "exists" on an integer in Perl, only on collections. Can you give an example of what you mean in Perl which matches your example in Java.
Given an expression that specifies a hash element or array element, returns true if the specified element in the hash or array has ever been initialized, even if the corresponding value is undefined.
This indicates it only applies to hash or array elements!
This should help.
Integer startIn = null;
// (optional below but a good practice, to prevent errors.)
boolean dontContinue = false;
try {
Integer.parseInt (startField.getText());
} catch (NumberFormatException e){
e.printStackTrace();
}
// in java = assigns a boolean in if statements oddly.
// Thus double equal must be used. So if startIn is null, display the message
if (startIn == null) {
JOptionPane.showMessageDialog(null,
"You must enter a number between 0-16.","Input Error",
JOptionPane.ERROR_MESSAGE);
}
// (again optional)
if (dontContinue == true) {
//Do-some-error-fix
}