How to validate a JSoup selection string - java

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;
}

Related

How do I make Selenium webdriver to wait for an element to change its attribute to something else in Java

Below is my element;
String sId = driver.findElement(By.xpath(path)).getAttribute("data-id");
Since now the attribute value is stored in "sId", I now need to tell Selenium to wait until data-id attribute value is NOT equal to sID.
I have tried the code below with no luck:
wait.until_not(ExpectedConditions.textToBePresentInElement(By.xpath(path), sId));
I get the error:
The method until_not(ExpectedCondition) is undefined for the
type WebDriverWait
Also even if I change "until_not" to "until" I get this warning:
The method textToBePresentInElement(By, String) from the type
ExpectedConditions is deprecated
How do I do it?
If you are checking the attribute value checking the text won't help you, those are two different things.
You can combine the not and attributeToBe ExpectedConditions for that
wait.until(ExpectedConditions.not(ExpectedConditions.attributeToBe(By.xpath(path), "data-id", sId)));
You can try to use not from Expected conditions, for example:
wait.until(ExpectedConditions.not(ExpectedConditions.textToBePresentInElement(<element>, <elementtext>)));
Or you can also use expected conditions invisibility method:
wait.until(ExpectedConditions.invisibilityOfElementWithText(locator, text));
You can try bellow code
String sId = driver.findElement(By.xpath(path)).getAttribute("data-id");
boolean expectedCondition = false;
boolean timeOut = false;
int second = 1;
do {
try {
//timeout for 60 seconds
if(second>60) {
timeOut = true;
}
String expectedId = driver.findElement(By.xpath(path)).getAttribute("data-id");
if(! expectedId.equals(sId)) {
expectedCondition=true;
}else {
second++;
}
} catch (Exception e) {
TimeUnit.SECONDS.sleep(1);
second++;
}
}while(expectedCondition==false && timeOut==false);

ANTLR3 parser fails when using memoize

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?

Converting A String to a Boolean -- Java

I've been searching around stackoverflow, and I've found a few other questions on converting a string to a boolean, but I can't make it work. Perhaps it is just the way I am trying to use it is incorrect.
Anyways, I am trying to convert two different input strings "M" or "I" in to boolean for use in an if statement. What is basically want the functionality to be is this:
// the text that is retrieved is assumed to be either"M" or "I"
M=Input.getText
I=Input.getText
If M shows the value "M",
do stuff here
else if I shows the value "I",
do stuff here
else if neither above are true,
throw an exception here
I've tried any number of "toBoolean"s and "Boolean.valueof"s, but none of what I try is working.
PS, Sorry for not having actual code to work with, this is my first step, and thus I haven't built anything up around this piece.
You can use String's methods to check for whether it contains a given literal value, equals it, or equals ignoring case.
A draft condition would be:
if ("myValue".equalsIgnoreCase(myText)) {
// TODO
}
else if ("myOtherValue".equalsIgnoreCase(myOtherText)) {
// TODO
}
else {
// TODO
}
Here is the documentation in java.lang.String:
equals
equalsIgnorecase
contains
You also want to check the many other methods, such as startsWith, endswith, etc. etc.
Use this for one boolean:
boolean b = (M.equals("M") || I.equals("I"));
Or this for two boolean:
boolean booleanM = (M.equals("M"));
boolean booleanI = (I.equals("I"));
if(booleanM){
//do stuff here
}else if(booleanI){
//do stuff here
}else{
//do stuff here where both are false
}
This is the faster way if you need to verify more than one time, only one time use this:
if(M.equals("M")){
//do stuff here
}else if(I.equals("I")){
//do stuff here
}else{
//do stuff here where both are false
}
You can simply use boolean b = Input.getText().equalsIgnoreCase("YourTrueString"). This method will detect if the input text is exactly the same as "YourTrueString". If not, it'll return false. This way anything that isn't true becomes false.
From your pseudo code
// the text that is retrieved is assumed to be either"M" or "I"
M=Input.getText
I=Input.getText
If M shows the value "M",
do stuff here
else if I shows the value "I",
do stuff here
else if neither above are true,
throw an exception here
To Java
// In its own method for reuse, in case you want to extend character support
public boolean match(String character, String match) {
return character.equals(match);
}
You can then invoke this simple method
String m = Input.getText();
String i = Input.getText();
if (match(m, "M")) {
do stuff here
} else if (match(i, "I")) {
do stuff here
} else {
throw an exception here
}

How to write it using streams? Java 8

I wrote a piece of code and wonder how I can write it more elegant, using streams
here it is:
public boolean possibleToAddTask(LocalDate taskDate, final String username) {
List<Task> userTasklist = find(username).getTaskList();
for(Task task : userTasklist) {
if(task.getDate().equals(taskDate)){
return false;
}
}
return true;
}
Here - some boolean is returned from a method. If specified date already exists in some task it returns false, otherwise true (so the return type answers the question raised in method's name :))
I was trying with filters on streams, but It worked just for a while, and then unit tests gave me some unexpected results so I deleted it and wrote it like Its upper. Now I want to beautify it
previously it was like this:
public boolean possibleToAddTask(LocalDate taskDate, final String username) {
List<Task> userTasklist = find(username).getTaskList();
try {
userTasklist.stream().filter(n -> n.getDate().equals(taskDate)).findFirst().get();
return true;
} catch (NoSuchElementException e) {
return false;
}
}
thanks in advance :)
Method findFirst() return an Optional. So you can just check if optional is empty.
return !userTasklist.stream()
.filter(n -> n.getDate().equals(taskDate))
.findFirst().isPresent();
Or even easier approach.
return !userTasklist.stream().anyMatch(n -> n.getDate().equals(taskDate));
EDIT: Now unit tests should pass.
How about doing something lik transforming the List into Set and then calling contains():
return userTasklist.stream().map(Task::getDate).collect(Collectors.toSet()).contains(taskDate);

How can I tell if a Java integer is null?

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
}

Categories