pull/1172/head
gedoor 3 years ago
parent 3568100f47
commit 34b771631f
  1. 1
      JsoupXpath/.gitignore
  2. 44
      JsoupXpath/build.gradle
  3. 0
      JsoupXpath/consumer-rules.pro
  4. 21
      JsoupXpath/proguard-rules.pro
  5. 4
      JsoupXpath/src/main/AndroidManifest.xml
  6. 152
      JsoupXpath/src/main/java/org/seimicrawler/xpath/JXDocument.java
  7. 130
      JsoupXpath/src/main/java/org/seimicrawler/xpath/JXNode.java
  8. 120
      JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/Xpath.interp
  9. 76
      JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/Xpath.tokens
  10. 535
      JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathBaseListener.java
  11. 311
      JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathBaseVisitor.java
  12. 143
      JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathLexer.interp
  13. 325
      JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathLexer.java
  14. 76
      JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathLexer.tokens
  15. 388
      JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathListener.java
  16. 2490
      JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathParser.java
  17. 229
      JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathVisitor.java
  18. 24
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/AxisSelector.java
  19. 10
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/Constants.java
  20. 13
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/Function.java
  21. 21
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/NodeTest.java
  22. 76
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/Scope.java
  23. 226
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/XValue.java
  24. 613
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/XpathProcessor.java
  25. 34
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/AncestorOrSelfSelector.java
  26. 33
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/AncestorSelector.java
  27. 32
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/AttributeSelector.java
  28. 28
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/ChildSelector.java
  29. 34
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/DescendantOrSelfSelector.java
  30. 37
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/DescendantSelector.java
  31. 51
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/FollowingSelector.java
  32. 44
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/FollowingSiblingOneSelector.java
  33. 48
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/FollowingSiblingSelector.java
  34. 34
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/ParentSelector.java
  35. 56
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/PrecedingSelector.java
  36. 44
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/PrecedingSiblingOneSelector.java
  37. 48
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/PrecedingSiblingSelector.java
  38. 23
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/SelfSelector.java
  39. 30
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Concat.java
  40. 29
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Contains.java
  41. 29
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Count.java
  42. 25
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/First.java
  43. 47
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/FormatDate.java
  44. 28
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Last.java
  45. 30
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Not.java
  46. 28
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Position.java
  47. 29
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/StartsWith.java
  48. 30
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/StringLength.java
  49. 47
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubString.java
  50. 32
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubStringAfter.java
  51. 32
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubStringAfterLast.java
  52. 33
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubStringBefore.java
  53. 33
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubStringBeforeLast.java
  54. 44
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubStringEx.java
  55. 44
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/AllText.java
  56. 31
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/Html.java
  57. 45
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/Node.java
  58. 51
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/Num.java
  59. 31
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/OuterHtml.java
  60. 108
      JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/Text.java
  61. 35
      JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/DoFailOnErrorHandler.java
  62. 28
      JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/NoSuchAxisException.java
  63. 26
      JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/NoSuchFunctionException.java
  64. 13
      JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/XpathMergeValueException.java
  65. 23
      JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/XpathParserException.java
  66. 30
      JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/XpathSyntaxErrorException.java
  67. 121
      JsoupXpath/src/main/java/org/seimicrawler/xpath/util/CommonUtil.java
  68. 141
      JsoupXpath/src/main/java/org/seimicrawler/xpath/util/Scanner.java
  69. 296
      JsoupXpath/src/main/resources/Xpath.g4
  70. 12
      JsoupXpath/src/test/java/org/seimicrawler/xpath/BaseTest.java
  71. 289
      JsoupXpath/src/test/java/org/seimicrawler/xpath/JXDocumentTest.java
  72. 49
      JsoupXpath/src/test/java/org/seimicrawler/xpath/core/function/DateFormatTest.java
  73. 53
      JsoupXpath/src/test/java/org/seimicrawler/xpath/core/function/SubStringTest.java
  74. 59
      JsoupXpath/src/test/java/org/seimicrawler/xpath/core/node/NumTest.java
  75. 63
      JsoupXpath/src/test/java/org/seimicrawler/xpath/expr/ExprTest.java
  76. 5416
      JsoupXpath/src/test/resources/d_test.html
  77. 2
      app/build.gradle
  78. 70
      app/src/main/res/layout/dialog_read_padding.xml
  79. 10
      app/src/main/res/layout/dialog_tip_config.xml
  80. 2
      settings.gradle

@ -1 +0,0 @@
/build

@ -1,44 +0,0 @@
plugins {
id 'com.android.library'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
android {
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation('org.jsoup:jsoup:1.14.1')
implementation('org.antlr:antlr4-runtime:4.7.2')
implementation('org.apache.commons:commons-lang3:3.11')
implementation('org.slf4j:slf4j-api:1.7.30')
implementation('org.slf4j:slf4j-simple:1.7.30')
testImplementation('ch.qos.logback:logback-core:1.2.3')
testImplementation('org.powermock:powermock-module-junit4:1.6.3')
testImplementation('org.powermock:powermock-api-mockito:1.6.3')
testImplementation('com.tngtech.java:junit-dataprovider:1.10.2')
testImplementation('commons-io:commons-io:2.6')
}

@ -1,21 +0,0 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="org.seimicrawler.xpath">
</manifest>

@ -1,152 +0,0 @@
package org.seimicrawler.xpath;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.seimicrawler.xpath.antlr.XpathLexer;
import org.seimicrawler.xpath.antlr.XpathParser;
import org.seimicrawler.xpath.core.XValue;
import org.seimicrawler.xpath.core.XpathProcessor;
import org.seimicrawler.xpath.exception.DoFailOnErrorHandler;
import org.seimicrawler.xpath.exception.XpathParserException;
import org.seimicrawler.xpath.exception.XpathSyntaxErrorException;
import java.util.LinkedList;
import java.util.List;
/*
Copyright 2014 Wang Haomiao<seimimaster@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
*/
public class JXDocument {
private final Elements elements;
public JXDocument(Elements els) {
elements = els;
}
public static JXDocument create(Document doc) {
Elements els = doc.children();
return new JXDocument(els);
}
public static JXDocument create(Elements els) {
return new JXDocument(els);
}
public static JXDocument create(String html) {
Elements els = Jsoup.parse(html).children();
return new JXDocument(els);
}
public static JXDocument createByUrl(String url) {
Elements els;
try {
els = Jsoup.connect(url).get().children();
} catch (Exception e) {
throw new XpathParserException("url资源获取失败", e);
}
return new JXDocument(els);
}
public List<Object> sel(String xpath) {
List<Object> res = new LinkedList<>();
for (JXNode node : selN(xpath)) {
if (node.isElement()) {
res.add(node.asElement());
} else {
res.add(node.toString());
}
}
return res;
}
public List<JXNode> selN(String xpath) {
List<JXNode> finalRes = new LinkedList<>();
try {
CharStream input = CharStreams.fromString(xpath);
XpathLexer lexer = new XpathLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
XpathParser parser = new XpathParser(tokens);
parser.setErrorHandler(new DoFailOnErrorHandler());
ParseTree tree = parser.main();
XpathProcessor processor = new XpathProcessor(elements);
XValue calRes = processor.visit(tree);
if (calRes.isElements()) {
for (Element el : calRes.asElements()) {
finalRes.add(JXNode.create(el));
}
return finalRes;
}
if (calRes.isList()) {
for (String str : calRes.asList()) {
finalRes.add(JXNode.create(str));
}
return finalRes;
}
if (calRes.isString()) {
finalRes.add(JXNode.create(calRes.asString()));
return finalRes;
}
if (calRes.isNumber()) {
finalRes.add(JXNode.create(calRes.asDouble()));
return finalRes;
}
if (calRes.isBoolean()) {
finalRes.add(JXNode.create(calRes.asBoolean()));
return finalRes;
}
if (calRes.isDate()) {
finalRes.add(JXNode.create(calRes.asDate()));
return finalRes;
}
finalRes.add(JXNode.create(calRes.asString()));
} catch (Exception e) {
String msg = "Please check the syntax of your xpath expr or commit a ";
throw new XpathSyntaxErrorException(msg + ExceptionUtils.getRootCauseMessage(e), e);
}
return finalRes;
}
public Object selOne(String xpath) {
JXNode jxNode = selNOne(xpath);
if (jxNode != null) {
if (jxNode.isElement()) {
return jxNode.asElement();
} else {
return jxNode.toString();
}
}
return null;
}
public JXNode selNOne(String xpath) {
List<JXNode> jxNodeList = selN(xpath);
if (jxNodeList != null && jxNodeList.size() > 0) {
return jxNodeList.get(0);
}
return null;
}
}

@ -1,130 +0,0 @@
package org.seimicrawler.xpath;
/*
Copyright 2014 Wang Haomiao<seimimaster@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.seimicrawler.xpath.core.Constants;
import java.util.Date;
import java.util.List;
import java.util.Objects;
/**
* XPath提取后的
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2016/5/12.
*/
public class JXNode {
private final Object value;
public JXNode(Object val) {
this.value = val;
}
public boolean isElement() {
return value instanceof Element;
}
public Element asElement() {
return (Element) value;
}
public boolean isString() {
return value instanceof String;
}
public String asString() {
if (isString()) {
return (String) value;
} else if (isElement()) {
Element e = (Element) value;
if (Objects.equals(e.tagName(), Constants.DEF_TEXT_TAG_NAME)) {
return e.ownText();
} else {
return e.toString();
}
} else {
return String.valueOf(value);
}
}
public boolean isNumber() {
return value instanceof Number;
}
public Double asDouble() {
return (Double) value;
}
public boolean isBoolean() {
return value instanceof Boolean;
}
public Boolean asBoolean() {
return (Boolean) value;
}
public boolean isDate() {
return value instanceof Date;
}
public Date asDate() {
return (Date) value;
}
public List<JXNode> sel(String xpath) {
if (!isElement()) {
return null;
}
JXDocument doc = new JXDocument(new Elements(asElement()));
return doc.selN(xpath);
}
public JXNode selOne(String xpath) {
List<JXNode> jxNodeList = sel(xpath);
if (jxNodeList != null && jxNodeList.size() > 0) {
return jxNodeList.get(0);
}
return null;
}
public static JXNode create(Object val) {
return new JXNode(val);
}
@Override
public String toString() {
return asString();
}
public Object value() {
if (isElement()) {
return asElement();
}
if (isBoolean()) {
return asBoolean();
}
if (isNumber()) {
return asDouble();
}
if (isDate()) {
return asDate();
}
return asString();
}
}

File diff suppressed because one or more lines are too long

@ -1,76 +0,0 @@
T__0=1
T__1=2
T__2=3
T__3=4
NodeType=5
Number=6
AxisName=7
PATHSEP=8
ABRPATH=9
LPAR=10
RPAR=11
LBRAC=12
RBRAC=13
MINUS=14
PLUS=15
DOT=16
MUL=17
DIVISION=18
MODULO=19
DOTDOT=20
AT=21
COMMA=22
PIPE=23
LESS=24
MORE_=25
LE=26
GE=27
EQUALITY=28
INEQUALITY=29
START_WITH=30
END_WITH=31
CONTAIN_WITH=32
REGEXP_WITH=33
REGEXP_NOT_WITH=34
COLON=35
CC=36
APOS=37
QUOT=38
Literal=39
Whitespace=40
NCName=41
'processing-instruction'=1
'or'=2
'and'=3
'$'=4
'/'=8
'//'=9
'('=10
')'=11
'['=12
']'=13
'-'=14
'+'=15
'.'=16
'*'=17
'`div`'=18
'`mod`'=19
'..'=20
'@'=21
','=22
'|'=23
'<'=24
'>'=25
'<='=26
'>='=27
'='=28
'!='=29
'^='=30
'$='=31
'*='=32
'~='=33
'!~'=34
':'=35
'::'=36
'\''=37
'"'=38

@ -1,535 +0,0 @@
// Generated from resources/Xpath.g4 by ANTLR 4.7.2
package org.seimicrawler.xpath.antlr;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.TerminalNode;
/**
* This class provides an empty implementation of {@link XpathListener},
* which can be extended to create a listener which only needs to handle a subset
* of the available methods.
*/
public class XpathBaseListener implements XpathListener {
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterMain(XpathParser.MainContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitMain(XpathParser.MainContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterLocationPath(XpathParser.LocationPathContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitLocationPath(XpathParser.LocationPathContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterAbsoluteLocationPathNoroot(XpathParser.AbsoluteLocationPathNorootContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitAbsoluteLocationPathNoroot(XpathParser.AbsoluteLocationPathNorootContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterRelativeLocationPath(XpathParser.RelativeLocationPathContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitRelativeLocationPath(XpathParser.RelativeLocationPathContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterStep(XpathParser.StepContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitStep(XpathParser.StepContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterAxisSpecifier(XpathParser.AxisSpecifierContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitAxisSpecifier(XpathParser.AxisSpecifierContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterNodeTest(XpathParser.NodeTestContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitNodeTest(XpathParser.NodeTestContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterPredicate(XpathParser.PredicateContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitPredicate(XpathParser.PredicateContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterAbbreviatedStep(XpathParser.AbbreviatedStepContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitAbbreviatedStep(XpathParser.AbbreviatedStepContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterExpr(XpathParser.ExprContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitExpr(XpathParser.ExprContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterPrimaryExpr(XpathParser.PrimaryExprContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitPrimaryExpr(XpathParser.PrimaryExprContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterFunctionCall(XpathParser.FunctionCallContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitFunctionCall(XpathParser.FunctionCallContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterUnionExprNoRoot(XpathParser.UnionExprNoRootContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitUnionExprNoRoot(XpathParser.UnionExprNoRootContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterPathExprNoRoot(XpathParser.PathExprNoRootContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitPathExprNoRoot(XpathParser.PathExprNoRootContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterFilterExpr(XpathParser.FilterExprContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitFilterExpr(XpathParser.FilterExprContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterOrExpr(XpathParser.OrExprContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitOrExpr(XpathParser.OrExprContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterAndExpr(XpathParser.AndExprContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitAndExpr(XpathParser.AndExprContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterEqualityExpr(XpathParser.EqualityExprContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitEqualityExpr(XpathParser.EqualityExprContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterRelationalExpr(XpathParser.RelationalExprContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitRelationalExpr(XpathParser.RelationalExprContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterAdditiveExpr(XpathParser.AdditiveExprContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitAdditiveExpr(XpathParser.AdditiveExprContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterMultiplicativeExpr(XpathParser.MultiplicativeExprContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitMultiplicativeExpr(XpathParser.MultiplicativeExprContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterUnaryExprNoRoot(XpathParser.UnaryExprNoRootContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitUnaryExprNoRoot(XpathParser.UnaryExprNoRootContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterQName(XpathParser.QNameContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitQName(XpathParser.QNameContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterFunctionName(XpathParser.FunctionNameContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitFunctionName(XpathParser.FunctionNameContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterVariableReference(XpathParser.VariableReferenceContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitVariableReference(XpathParser.VariableReferenceContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterNameTest(XpathParser.NameTestContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitNameTest(XpathParser.NameTestContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterNCName(XpathParser.NCNameContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitNCName(XpathParser.NCNameContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void enterEveryRule(ParserRuleContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void exitEveryRule(ParserRuleContext ctx) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void visitTerminal(TerminalNode node) {
}
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override
public void visitErrorNode(ErrorNode node) {
}
}

@ -1,311 +0,0 @@
// Generated from resources/Xpath.g4 by ANTLR 4.7.2
package org.seimicrawler.xpath.antlr;
import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor;
/**
* This class provides an empty implementation of {@link XpathVisitor},
* which can be extended to create a visitor which only needs to handle a subset
* of the available methods.
*
* @param <T> The return type of the visit operation. Use {@link Void} for
* operations with no return type.
*/
public class XpathBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements XpathVisitor<T> {
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitMain(XpathParser.MainContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitLocationPath(XpathParser.LocationPathContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitAbsoluteLocationPathNoroot(XpathParser.AbsoluteLocationPathNorootContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitRelativeLocationPath(XpathParser.RelativeLocationPathContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitStep(XpathParser.StepContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitAxisSpecifier(XpathParser.AxisSpecifierContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitNodeTest(XpathParser.NodeTestContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitPredicate(XpathParser.PredicateContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitAbbreviatedStep(XpathParser.AbbreviatedStepContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitExpr(XpathParser.ExprContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitPrimaryExpr(XpathParser.PrimaryExprContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitFunctionCall(XpathParser.FunctionCallContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitUnionExprNoRoot(XpathParser.UnionExprNoRootContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitPathExprNoRoot(XpathParser.PathExprNoRootContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitFilterExpr(XpathParser.FilterExprContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitOrExpr(XpathParser.OrExprContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitAndExpr(XpathParser.AndExprContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitEqualityExpr(XpathParser.EqualityExprContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitRelationalExpr(XpathParser.RelationalExprContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitAdditiveExpr(XpathParser.AdditiveExprContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitMultiplicativeExpr(XpathParser.MultiplicativeExprContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitUnaryExprNoRoot(XpathParser.UnaryExprNoRootContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitQName(XpathParser.QNameContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitFunctionName(XpathParser.FunctionNameContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitVariableReference(XpathParser.VariableReferenceContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitNameTest(XpathParser.NameTestContext ctx) {
return visitChildren(ctx);
}
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override
public T visitNCName(XpathParser.NCNameContext ctx) {
return visitChildren(ctx);
}
}

File diff suppressed because one or more lines are too long

@ -1,325 +0,0 @@
// Generated from resources/Xpath.g4 by ANTLR 4.7.2
package org.seimicrawler.xpath.antlr;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.Lexer;
import org.antlr.v4.runtime.RuntimeMetaData;
import org.antlr.v4.runtime.Vocabulary;
import org.antlr.v4.runtime.VocabularyImpl;
import org.antlr.v4.runtime.atn.ATN;
import org.antlr.v4.runtime.atn.ATNDeserializer;
import org.antlr.v4.runtime.atn.LexerATNSimulator;
import org.antlr.v4.runtime.atn.PredictionContextCache;
import org.antlr.v4.runtime.dfa.DFA;
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
public class XpathLexer extends Lexer {
static {
RuntimeMetaData.checkVersion("4.7.2", RuntimeMetaData.VERSION);
}
protected static final DFA[] _decisionToDFA;
protected static final PredictionContextCache _sharedContextCache =
new PredictionContextCache();
public static final int
T__0 = 1, T__1 = 2, T__2 = 3, T__3 = 4, NodeType = 5, Number = 6, AxisName = 7, PATHSEP = 8,
ABRPATH = 9, LPAR = 10, RPAR = 11, LBRAC = 12, RBRAC = 13, MINUS = 14, PLUS = 15, DOT = 16,
MUL = 17, DIVISION = 18, MODULO = 19, DOTDOT = 20, AT = 21, COMMA = 22, PIPE = 23, LESS = 24,
MORE_ = 25, LE = 26, GE = 27, EQUALITY = 28, INEQUALITY = 29, START_WITH = 30, END_WITH = 31,
CONTAIN_WITH = 32, REGEXP_WITH = 33, REGEXP_NOT_WITH = 34, COLON = 35, CC = 36,
APOS = 37, QUOT = 38, Literal = 39, Whitespace = 40, NCName = 41;
public static String[] channelNames = {
"DEFAULT_TOKEN_CHANNEL", "HIDDEN"
};
public static String[] modeNames = {
"DEFAULT_MODE"
};
private static String[] makeRuleNames() {
return new String[]{
"T__0", "T__1", "T__2", "T__3", "NodeType", "Number", "Digits", "AxisName",
"PATHSEP", "ABRPATH", "LPAR", "RPAR", "LBRAC", "RBRAC", "MINUS", "PLUS",
"DOT", "MUL", "DIVISION", "MODULO", "DOTDOT", "AT", "COMMA", "PIPE",
"LESS", "MORE_", "LE", "GE", "EQUALITY", "INEQUALITY", "START_WITH",
"END_WITH", "CONTAIN_WITH", "REGEXP_WITH", "REGEXP_NOT_WITH", "COLON",
"CC", "APOS", "QUOT", "Literal", "Whitespace", "NCName", "NCNameStartChar",
"NCNameChar"
};
}
public static final String[] ruleNames = makeRuleNames();
private static String[] makeLiteralNames() {
return new String[]{
null, "'processing-instruction'", "'or'", "'and'", "'$'", null, null,
null, "'/'", "'//'", "'('", "')'", "'['", "']'", "'-'", "'+'", "'.'",
"'*'", "'`div`'", "'`mod`'", "'..'", "'@'", "','", "'|'", "'<'", "'>'",
"'<='", "'>='", "'='", "'!='", "'^='", "'$='", "'*='", "'~='", "'!~'",
"':'", "'::'", "'''", "'\"'"
};
}
private static final String[] _LITERAL_NAMES = makeLiteralNames();
private static String[] makeSymbolicNames() {
return new String[]{
null, null, null, null, null, "NodeType", "Number", "AxisName", "PATHSEP",
"ABRPATH", "LPAR", "RPAR", "LBRAC", "RBRAC", "MINUS", "PLUS", "DOT",
"MUL", "DIVISION", "MODULO", "DOTDOT", "AT", "COMMA", "PIPE", "LESS",
"MORE_", "LE", "GE", "EQUALITY", "INEQUALITY", "START_WITH", "END_WITH",
"CONTAIN_WITH", "REGEXP_WITH", "REGEXP_NOT_WITH", "COLON", "CC", "APOS",
"QUOT", "Literal", "Whitespace", "NCName"
};
}
private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames();
public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES);
/**
* @deprecated Use {@link #VOCABULARY} instead.
*/
@Deprecated
public static final String[] tokenNames;
static {
tokenNames = new String[_SYMBOLIC_NAMES.length];
for (int i = 0; i < tokenNames.length; i++) {
tokenNames[i] = VOCABULARY.getLiteralName(i);
if (tokenNames[i] == null) {
tokenNames[i] = VOCABULARY.getSymbolicName(i);
}
if (tokenNames[i] == null) {
tokenNames[i] = "<INVALID>";
}
}
}
@Override
@Deprecated
public String[] getTokenNames() {
return tokenNames;
}
@Override
public Vocabulary getVocabulary() {
return VOCABULARY;
}
public XpathLexer(CharStream input) {
super(input);
_interp = new LexerATNSimulator(this, _ATN, _decisionToDFA, _sharedContextCache);
}
@Override
public String getGrammarFileName() {
return "Xpath.g4";
}
@Override
public String[] getRuleNames() {
return ruleNames;
}
@Override
public String getSerializedATN() {
return _serializedATN;
}
@Override
public String[] getChannelNames() {
return channelNames;
}
@Override
public String[] getModeNames() {
return modeNames;
}
@Override
public ATN getATN() {
return _ATN;
}
public static final String _serializedATN =
"\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2+\u01f3\b\1\4\2\t" +
"\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13" +
"\t\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22" +
"\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31\t\31" +
"\4\32\t\32\4\33\t\33\4\34\t\34\4\35\t\35\4\36\t\36\4\37\t\37\4 \t \4!" +
"\t!\4\"\t\"\4#\t#\4$\t$\4%\t%\4&\t&\4\'\t\'\4(\t(\4)\t)\4*\t*\4+\t+\4" +
",\t,\4-\t-\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2" +
"\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\3\3\3\3\3\3\4\3\4\3\4\3\4\3\5\3\5\3" +
"\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6" +
"\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3" +
"\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6" +
"\3\6\3\6\3\6\3\6\3\6\3\6\3\6\5\6\u00b8\n\6\3\7\3\7\3\7\5\7\u00bd\n\7\5" +
"\7\u00bf\n\7\3\7\3\7\5\7\u00c3\n\7\3\b\6\b\u00c6\n\b\r\b\16\b\u00c7\3" +
"\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t" +
"\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3" +
"\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t" +
"\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3" +
"\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t" +
"\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3" +
"\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t" +
"\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3" +
"\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t" +
"\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3" +
"\t\3\t\5\t\u017b\n\t\3\n\3\n\3\13\3\13\3\13\3\f\3\f\3\r\3\r\3\16\3\16" +
"\3\17\3\17\3\20\3\20\3\21\3\21\3\22\3\22\3\23\3\23\3\24\3\24\3\24\3\24" +
"\3\24\3\24\3\25\3\25\3\25\3\25\3\25\3\25\3\26\3\26\3\26\3\27\3\27\3\30" +
"\3\30\3\31\3\31\3\32\3\32\3\33\3\33\3\34\3\34\3\34\3\35\3\35\3\35\3\36" +
"\3\36\3\37\3\37\3\37\3 \3 \3 \3!\3!\3!\3\"\3\"\3\"\3#\3#\3#\3$\3$\3$\3" +
"%\3%\3&\3&\3&\3\'\3\'\3(\3(\3)\3)\7)\u01d0\n)\f)\16)\u01d3\13)\3)\3)\3" +
")\7)\u01d8\n)\f)\16)\u01db\13)\3)\5)\u01de\n)\3*\6*\u01e1\n*\r*\16*\u01e2" +
"\3*\3*\3+\3+\7+\u01e9\n+\f+\16+\u01ec\13+\3,\3,\3-\3-\5-\u01f2\n-\2\2" +
".\3\3\5\4\7\5\t\6\13\7\r\b\17\2\21\t\23\n\25\13\27\f\31\r\33\16\35\17" +
"\37\20!\21#\22%\23\'\24)\25+\26-\27/\30\61\31\63\32\65\33\67\349\35;\36" +
"=\37? A!C\"E#G$I%K&M\'O(Q)S*U+W\2Y\2\3\2\7\3\2$$\3\2))\5\2\13\f\17\17" +
"\"\"\20\2C\\aac|\u00c2\u00d8\u00da\u00f8\u00fa\u0301\u0372\u037f\u0381" +
"\u2001\u200e\u200f\u2072\u2191\u2c02\u2ff1\u3003\ud801\uf902\ufdd1\ufdf2" +
"\uffff\7\2/\60\62;\u00b9\u00b9\u0302\u0371\u2041\u2042\2\u020e\2\3\3\2" +
"\2\2\2\5\3\2\2\2\2\7\3\2\2\2\2\t\3\2\2\2\2\13\3\2\2\2\2\r\3\2\2\2\2\21" +
"\3\2\2\2\2\23\3\2\2\2\2\25\3\2\2\2\2\27\3\2\2\2\2\31\3\2\2\2\2\33\3\2" +
"\2\2\2\35\3\2\2\2\2\37\3\2\2\2\2!\3\2\2\2\2#\3\2\2\2\2%\3\2\2\2\2\'\3" +
"\2\2\2\2)\3\2\2\2\2+\3\2\2\2\2-\3\2\2\2\2/\3\2\2\2\2\61\3\2\2\2\2\63\3" +
"\2\2\2\2\65\3\2\2\2\2\67\3\2\2\2\29\3\2\2\2\2;\3\2\2\2\2=\3\2\2\2\2?\3" +
"\2\2\2\2A\3\2\2\2\2C\3\2\2\2\2E\3\2\2\2\2G\3\2\2\2\2I\3\2\2\2\2K\3\2\2" +
"\2\2M\3\2\2\2\2O\3\2\2\2\2Q\3\2\2\2\2S\3\2\2\2\2U\3\2\2\2\3[\3\2\2\2\5" +
"r\3\2\2\2\7u\3\2\2\2\ty\3\2\2\2\13\u00b7\3\2\2\2\r\u00c2\3\2\2\2\17\u00c5" +
"\3\2\2\2\21\u017a\3\2\2\2\23\u017c\3\2\2\2\25\u017e\3\2\2\2\27\u0181\3" +
"\2\2\2\31\u0183\3\2\2\2\33\u0185\3\2\2\2\35\u0187\3\2\2\2\37\u0189\3\2" +
"\2\2!\u018b\3\2\2\2#\u018d\3\2\2\2%\u018f\3\2\2\2\'\u0191\3\2\2\2)\u0197" +
"\3\2\2\2+\u019d\3\2\2\2-\u01a0\3\2\2\2/\u01a2\3\2\2\2\61\u01a4\3\2\2\2" +
"\63\u01a6\3\2\2\2\65\u01a8\3\2\2\2\67\u01aa\3\2\2\29\u01ad\3\2\2\2;\u01b0" +
"\3\2\2\2=\u01b2\3\2\2\2?\u01b5\3\2\2\2A\u01b8\3\2\2\2C\u01bb\3\2\2\2E" +
"\u01be\3\2\2\2G\u01c1\3\2\2\2I\u01c4\3\2\2\2K\u01c6\3\2\2\2M\u01c9\3\2" +
"\2\2O\u01cb\3\2\2\2Q\u01dd\3\2\2\2S\u01e0\3\2\2\2U\u01e6\3\2\2\2W\u01ed" +
"\3\2\2\2Y\u01f1\3\2\2\2[\\\7r\2\2\\]\7t\2\2]^\7q\2\2^_\7e\2\2_`\7g\2\2" +
"`a\7u\2\2ab\7u\2\2bc\7k\2\2cd\7p\2\2de\7i\2\2ef\7/\2\2fg\7k\2\2gh\7p\2" +
"\2hi\7u\2\2ij\7v\2\2jk\7t\2\2kl\7w\2\2lm\7e\2\2mn\7v\2\2no\7k\2\2op\7" +
"q\2\2pq\7p\2\2q\4\3\2\2\2rs\7q\2\2st\7t\2\2t\6\3\2\2\2uv\7c\2\2vw\7p\2" +
"\2wx\7f\2\2x\b\3\2\2\2yz\7&\2\2z\n\3\2\2\2{|\7e\2\2|}\7q\2\2}~\7o\2\2" +
"~\177\7o\2\2\177\u0080\7g\2\2\u0080\u0081\7p\2\2\u0081\u00b8\7v\2\2\u0082" +
"\u0083\7v\2\2\u0083\u0084\7g\2\2\u0084\u0085\7z\2\2\u0085\u00b8\7v\2\2" +
"\u0086\u0087\7r\2\2\u0087\u0088\7t\2\2\u0088\u0089\7q\2\2\u0089\u008a" +
"\7e\2\2\u008a\u008b\7g\2\2\u008b\u008c\7u\2\2\u008c\u008d\7u\2\2\u008d" +
"\u008e\7k\2\2\u008e\u008f\7p\2\2\u008f\u0090\7i\2\2\u0090\u0091\7/\2\2" +
"\u0091\u0092\7k\2\2\u0092\u0093\7p\2\2\u0093\u0094\7u\2\2\u0094\u0095" +
"\7v\2\2\u0095\u0096\7t\2\2\u0096\u0097\7w\2\2\u0097\u0098\7e\2\2\u0098" +
"\u0099\7v\2\2\u0099\u009a\7k\2\2\u009a\u009b\7q\2\2\u009b\u00b8\7p\2\2" +
"\u009c\u009d\7p\2\2\u009d\u009e\7q\2\2\u009e\u009f\7f\2\2\u009f\u00b8" +
"\7g\2\2\u00a0\u00a1\7p\2\2\u00a1\u00a2\7w\2\2\u00a2\u00b8\7o\2\2\u00a3" +
"\u00a4\7c\2\2\u00a4\u00a5\7n\2\2\u00a5\u00a6\7n\2\2\u00a6\u00a7\7V\2\2" +
"\u00a7\u00a8\7g\2\2\u00a8\u00a9\7z\2\2\u00a9\u00b8\7v\2\2\u00aa\u00ab" +
"\7q\2\2\u00ab\u00ac\7w\2\2\u00ac\u00ad\7v\2\2\u00ad\u00ae\7g\2\2\u00ae" +
"\u00af\7t\2\2\u00af\u00b0\7J\2\2\u00b0\u00b1\7v\2\2\u00b1\u00b2\7o\2\2" +
"\u00b2\u00b8\7n\2\2\u00b3\u00b4\7j\2\2\u00b4\u00b5\7v\2\2\u00b5\u00b6" +
"\7o\2\2\u00b6\u00b8\7n\2\2\u00b7{\3\2\2\2\u00b7\u0082\3\2\2\2\u00b7\u0086" +
"\3\2\2\2\u00b7\u009c\3\2\2\2\u00b7\u00a0\3\2\2\2\u00b7\u00a3\3\2\2\2\u00b7" +
"\u00aa\3\2\2\2\u00b7\u00b3\3\2\2\2\u00b8\f\3\2\2\2\u00b9\u00be\5\17\b" +
"\2\u00ba\u00bc\7\60\2\2\u00bb\u00bd\5\17\b\2\u00bc\u00bb\3\2\2\2\u00bc" +
"\u00bd\3\2\2\2\u00bd\u00bf\3\2\2\2\u00be\u00ba\3\2\2\2\u00be\u00bf\3\2" +
"\2\2\u00bf\u00c3\3\2\2\2\u00c0\u00c1\7\60\2\2\u00c1\u00c3\5\17\b\2\u00c2" +
"\u00b9\3\2\2\2\u00c2\u00c0\3\2\2\2\u00c3\16\3\2\2\2\u00c4\u00c6\4\62;" +
"\2\u00c5\u00c4\3\2\2\2\u00c6\u00c7\3\2\2\2\u00c7\u00c5\3\2\2\2\u00c7\u00c8" +
"\3\2\2\2\u00c8\20\3\2\2\2\u00c9\u00ca\7c\2\2\u00ca\u00cb\7p\2\2\u00cb" +
"\u00cc\7e\2\2\u00cc\u00cd\7g\2\2\u00cd\u00ce\7u\2\2\u00ce\u00cf\7v\2\2" +
"\u00cf\u00d0\7q\2\2\u00d0\u017b\7t\2\2\u00d1\u00d2\7c\2\2\u00d2\u00d3" +
"\7p\2\2\u00d3\u00d4\7e\2\2\u00d4\u00d5\7g\2\2\u00d5\u00d6\7u\2\2\u00d6" +
"\u00d7\7v\2\2\u00d7\u00d8\7q\2\2\u00d8\u00d9\7t\2\2\u00d9\u00da\7/\2\2" +
"\u00da\u00db\7q\2\2\u00db\u00dc\7t\2\2\u00dc\u00dd\7/\2\2\u00dd\u00de" +
"\7u\2\2\u00de\u00df\7g\2\2\u00df\u00e0\7n\2\2\u00e0\u017b\7h\2\2\u00e1" +
"\u00e2\7c\2\2\u00e2\u00e3\7v\2\2\u00e3\u00e4\7v\2\2\u00e4\u00e5\7t\2\2" +
"\u00e5\u00e6\7k\2\2\u00e6\u00e7\7d\2\2\u00e7\u00e8\7w\2\2\u00e8\u00e9" +
"\7v\2\2\u00e9\u017b\7g\2\2\u00ea\u00eb\7e\2\2\u00eb\u00ec\7j\2\2\u00ec" +
"\u00ed\7k\2\2\u00ed\u00ee\7n\2\2\u00ee\u017b\7f\2\2\u00ef\u00f0\7f\2\2" +
"\u00f0\u00f1\7g\2\2\u00f1\u00f2\7u\2\2\u00f2\u00f3\7e\2\2\u00f3\u00f4" +
"\7g\2\2\u00f4\u00f5\7p\2\2\u00f5\u00f6\7f\2\2\u00f6\u00f7\7c\2\2\u00f7" +
"\u00f8\7p\2\2\u00f8\u017b\7v\2\2\u00f9\u00fa\7f\2\2\u00fa\u00fb\7g\2\2" +
"\u00fb\u00fc\7u\2\2\u00fc\u00fd\7e\2\2\u00fd\u00fe\7g\2\2\u00fe\u00ff" +
"\7p\2\2\u00ff\u0100\7f\2\2\u0100\u0101\7c\2\2\u0101\u0102\7p\2\2\u0102" +
"\u0103\7v\2\2\u0103\u0104\7/\2\2\u0104\u0105\7q\2\2\u0105\u0106\7t\2\2" +
"\u0106\u0107\7/\2\2\u0107\u0108\7u\2\2\u0108\u0109\7g\2\2\u0109\u010a" +
"\7n\2\2\u010a\u017b\7h\2\2\u010b\u010c\7h\2\2\u010c\u010d\7q\2\2\u010d" +
"\u010e\7n\2\2\u010e\u010f\7n\2\2\u010f\u0110\7q\2\2\u0110\u0111\7y\2\2" +
"\u0111\u0112\7k\2\2\u0112\u0113\7p\2\2\u0113\u017b\7i\2\2\u0114\u0115" +
"\7h\2\2\u0115\u0116\7q\2\2\u0116\u0117\7n\2\2\u0117\u0118\7n\2\2\u0118" +
"\u0119\7q\2\2\u0119\u011a\7y\2\2\u011a\u011b\7k\2\2\u011b\u011c\7p\2\2" +
"\u011c\u011d\7i\2\2\u011d\u011e\7/\2\2\u011e\u011f\7u\2\2\u011f\u0120" +
"\7k\2\2\u0120\u0121\7d\2\2\u0121\u0122\7n\2\2\u0122\u0123\7k\2\2\u0123" +
"\u0124\7p\2\2\u0124\u017b\7i\2\2\u0125\u0126\7r\2\2\u0126\u0127\7c\2\2" +
"\u0127\u0128\7t\2\2\u0128\u0129\7g\2\2\u0129\u012a\7p\2\2\u012a\u017b" +
"\7v\2\2\u012b\u012c\7r\2\2\u012c\u012d\7t\2\2\u012d\u012e\7g\2\2\u012e" +
"\u012f\7e\2\2\u012f\u0130\7g\2\2\u0130\u0131\7f\2\2\u0131\u0132\7k\2\2" +
"\u0132\u0133\7p\2\2\u0133\u017b\7i\2\2\u0134\u0135\7r\2\2\u0135\u0136" +
"\7t\2\2\u0136\u0137\7g\2\2\u0137\u0138\7e\2\2\u0138\u0139\7g\2\2\u0139" +
"\u013a\7f\2\2\u013a\u013b\7k\2\2\u013b\u013c\7p\2\2\u013c\u013d\7i\2\2" +
"\u013d\u013e\7/\2\2\u013e\u013f\7u\2\2\u013f\u0140\7k\2\2\u0140\u0141" +
"\7d\2\2\u0141\u0142\7n\2\2\u0142\u0143\7k\2\2\u0143\u0144\7p\2\2\u0144" +
"\u017b\7i\2\2\u0145\u0146\7u\2\2\u0146\u0147\7g\2\2\u0147\u0148\7n\2\2" +
"\u0148\u017b\7h\2\2\u0149\u014a\7h\2\2\u014a\u014b\7q\2\2\u014b\u014c" +
"\7n\2\2\u014c\u014d\7n\2\2\u014d\u014e\7q\2\2\u014e\u014f\7y\2\2\u014f" +
"\u0150\7k\2\2\u0150\u0151\7p\2\2\u0151\u0152\7i\2\2\u0152\u0153\7/\2\2" +
"\u0153\u0154\7u\2\2\u0154\u0155\7k\2\2\u0155\u0156\7d\2\2\u0156\u0157" +
"\7n\2\2\u0157\u0158\7k\2\2\u0158\u0159\7p\2\2\u0159\u015a\7i\2\2\u015a" +
"\u015b\7/\2\2\u015b\u015c\7q\2\2\u015c\u015d\7p\2\2\u015d\u017b\7g\2\2" +
"\u015e\u015f\7r\2\2\u015f\u0160\7t\2\2\u0160\u0161\7g\2\2\u0161\u0162" +
"\7e\2\2\u0162\u0163\7g\2\2\u0163\u0164\7f\2\2\u0164\u0165\7k\2\2\u0165" +
"\u0166\7p\2\2\u0166\u0167\7i\2\2\u0167\u0168\7/\2\2\u0168\u0169\7u\2\2" +
"\u0169\u016a\7k\2\2\u016a\u016b\7d\2\2\u016b\u016c\7n\2\2\u016c\u016d" +
"\7k\2\2\u016d\u016e\7p\2\2\u016e\u016f\7i\2\2\u016f\u0170\7/\2\2\u0170" +
"\u0171\7q\2\2\u0171\u0172\7p\2\2\u0172\u017b\7g\2\2\u0173\u0174\7u\2\2" +
"\u0174\u0175\7k\2\2\u0175\u0176\7d\2\2\u0176\u0177\7n\2\2\u0177\u0178" +
"\7k\2\2\u0178\u0179\7p\2\2\u0179\u017b\7i\2\2\u017a\u00c9\3\2\2\2\u017a" +
"\u00d1\3\2\2\2\u017a\u00e1\3\2\2\2\u017a\u00ea\3\2\2\2\u017a\u00ef\3\2" +
"\2\2\u017a\u00f9\3\2\2\2\u017a\u010b\3\2\2\2\u017a\u0114\3\2\2\2\u017a" +
"\u0125\3\2\2\2\u017a\u012b\3\2\2\2\u017a\u0134\3\2\2\2\u017a\u0145\3\2" +
"\2\2\u017a\u0149\3\2\2\2\u017a\u015e\3\2\2\2\u017a\u0173\3\2\2\2\u017b" +
"\22\3\2\2\2\u017c\u017d\7\61\2\2\u017d\24\3\2\2\2\u017e\u017f\7\61\2\2" +
"\u017f\u0180\7\61\2\2\u0180\26\3\2\2\2\u0181\u0182\7*\2\2\u0182\30\3\2" +
"\2\2\u0183\u0184\7+\2\2\u0184\32\3\2\2\2\u0185\u0186\7]\2\2\u0186\34\3" +
"\2\2\2\u0187\u0188\7_\2\2\u0188\36\3\2\2\2\u0189\u018a\7/\2\2\u018a \3" +
"\2\2\2\u018b\u018c\7-\2\2\u018c\"\3\2\2\2\u018d\u018e\7\60\2\2\u018e$" +
"\3\2\2\2\u018f\u0190\7,\2\2\u0190&\3\2\2\2\u0191\u0192\7b\2\2\u0192\u0193" +
"\7f\2\2\u0193\u0194\7k\2\2\u0194\u0195\7x\2\2\u0195\u0196\7b\2\2\u0196" +
"(\3\2\2\2\u0197\u0198\7b\2\2\u0198\u0199\7o\2\2\u0199\u019a\7q\2\2\u019a" +
"\u019b\7f\2\2\u019b\u019c\7b\2\2\u019c*\3\2\2\2\u019d\u019e\7\60\2\2\u019e" +
"\u019f\7\60\2\2\u019f,\3\2\2\2\u01a0\u01a1\7B\2\2\u01a1.\3\2\2\2\u01a2" +
"\u01a3\7.\2\2\u01a3\60\3\2\2\2\u01a4\u01a5\7~\2\2\u01a5\62\3\2\2\2\u01a6" +
"\u01a7\7>\2\2\u01a7\64\3\2\2\2\u01a8\u01a9\7@\2\2\u01a9\66\3\2\2\2\u01aa" +
"\u01ab\7>\2\2\u01ab\u01ac\7?\2\2\u01ac8\3\2\2\2\u01ad\u01ae\7@\2\2\u01ae" +
"\u01af\7?\2\2\u01af:\3\2\2\2\u01b0\u01b1\7?\2\2\u01b1<\3\2\2\2\u01b2\u01b3" +
"\7#\2\2\u01b3\u01b4\7?\2\2\u01b4>\3\2\2\2\u01b5\u01b6\7`\2\2\u01b6\u01b7" +
"\7?\2\2\u01b7@\3\2\2\2\u01b8\u01b9\7&\2\2\u01b9\u01ba\7?\2\2\u01baB\3" +
"\2\2\2\u01bb\u01bc\7,\2\2\u01bc\u01bd\7?\2\2\u01bdD\3\2\2\2\u01be\u01bf" +
"\7\u0080\2\2\u01bf\u01c0\7?\2\2\u01c0F\3\2\2\2\u01c1\u01c2\7#\2\2\u01c2" +
"\u01c3\7\u0080\2\2\u01c3H\3\2\2\2\u01c4\u01c5\7<\2\2\u01c5J\3\2\2\2\u01c6" +
"\u01c7\7<\2\2\u01c7\u01c8\7<\2\2\u01c8L\3\2\2\2\u01c9\u01ca\7)\2\2\u01ca" +
"N\3\2\2\2\u01cb\u01cc\7$\2\2\u01ccP\3\2\2\2\u01cd\u01d1\7$\2\2\u01ce\u01d0" +
"\n\2\2\2\u01cf\u01ce\3\2\2\2\u01d0\u01d3\3\2\2\2\u01d1\u01cf\3\2\2\2\u01d1" +
"\u01d2\3\2\2\2\u01d2\u01d4\3\2\2\2\u01d3\u01d1\3\2\2\2\u01d4\u01de\7$" +
"\2\2\u01d5\u01d9\7)\2\2\u01d6\u01d8\n\3\2\2\u01d7\u01d6\3\2\2\2\u01d8" +
"\u01db\3\2\2\2\u01d9\u01d7\3\2\2\2\u01d9\u01da\3\2\2\2\u01da\u01dc\3\2" +
"\2\2\u01db\u01d9\3\2\2\2\u01dc\u01de\7)\2\2\u01dd\u01cd\3\2\2\2\u01dd" +
"\u01d5\3\2\2\2\u01deR\3\2\2\2\u01df\u01e1\t\4\2\2\u01e0\u01df\3\2\2\2" +
"\u01e1\u01e2\3\2\2\2\u01e2\u01e0\3\2\2\2\u01e2\u01e3\3\2\2\2\u01e3\u01e4" +
"\3\2\2\2\u01e4\u01e5\b*\2\2\u01e5T\3\2\2\2\u01e6\u01ea\5W,\2\u01e7\u01e9" +
"\5Y-\2\u01e8\u01e7\3\2\2\2\u01e9\u01ec\3\2\2\2\u01ea\u01e8\3\2\2\2\u01ea" +
"\u01eb\3\2\2\2\u01ebV\3\2\2\2\u01ec\u01ea\3\2\2\2\u01ed\u01ee\t\5\2\2" +
"\u01eeX\3\2\2\2\u01ef\u01f2\5W,\2\u01f0\u01f2\t\6\2\2\u01f1\u01ef\3\2" +
"\2\2\u01f1\u01f0\3\2\2\2\u01f2Z\3\2\2\2\17\2\u00b7\u00bc\u00be\u00c2\u00c7" +
"\u017a\u01d1\u01d9\u01dd\u01e2\u01ea\u01f1\3\b\2\2";
public static final ATN _ATN =
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
static {
_decisionToDFA = new DFA[_ATN.getNumberOfDecisions()];
for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) {
_decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i);
}
}
}

@ -1,76 +0,0 @@
T__0=1
T__1=2
T__2=3
T__3=4
NodeType=5
Number=6
AxisName=7
PATHSEP=8
ABRPATH=9
LPAR=10
RPAR=11
LBRAC=12
RBRAC=13
MINUS=14
PLUS=15
DOT=16
MUL=17
DIVISION=18
MODULO=19
DOTDOT=20
AT=21
COMMA=22
PIPE=23
LESS=24
MORE_=25
LE=26
GE=27
EQUALITY=28
INEQUALITY=29
START_WITH=30
END_WITH=31
CONTAIN_WITH=32
REGEXP_WITH=33
REGEXP_NOT_WITH=34
COLON=35
CC=36
APOS=37
QUOT=38
Literal=39
Whitespace=40
NCName=41
'processing-instruction'=1
'or'=2
'and'=3
'$'=4
'/'=8
'//'=9
'('=10
')'=11
'['=12
']'=13
'-'=14
'+'=15
'.'=16
'*'=17
'`div`'=18
'`mod`'=19
'..'=20
'@'=21
','=22
'|'=23
'<'=24
'>'=25
'<='=26
'>='=27
'='=28
'!='=29
'^='=30
'$='=31
'*='=32
'~='=33
'!~'=34
':'=35
'::'=36
'\''=37
'"'=38

@ -1,388 +0,0 @@
// Generated from resources/Xpath.g4 by ANTLR 4.7.2
package org.seimicrawler.xpath.antlr;
import org.antlr.v4.runtime.tree.ParseTreeListener;
/**
* This interface defines a complete listener for a parse tree produced by
* {@link XpathParser}.
*/
public interface XpathListener extends ParseTreeListener {
/**
* Enter a parse tree produced by {@link XpathParser#main}.
*
* @param ctx the parse tree
*/
void enterMain(XpathParser.MainContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#main}.
*
* @param ctx the parse tree
*/
void exitMain(XpathParser.MainContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#locationPath}.
*
* @param ctx the parse tree
*/
void enterLocationPath(XpathParser.LocationPathContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#locationPath}.
*
* @param ctx the parse tree
*/
void exitLocationPath(XpathParser.LocationPathContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#absoluteLocationPathNoroot}.
*
* @param ctx the parse tree
*/
void enterAbsoluteLocationPathNoroot(XpathParser.AbsoluteLocationPathNorootContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#absoluteLocationPathNoroot}.
*
* @param ctx the parse tree
*/
void exitAbsoluteLocationPathNoroot(XpathParser.AbsoluteLocationPathNorootContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#relativeLocationPath}.
*
* @param ctx the parse tree
*/
void enterRelativeLocationPath(XpathParser.RelativeLocationPathContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#relativeLocationPath}.
*
* @param ctx the parse tree
*/
void exitRelativeLocationPath(XpathParser.RelativeLocationPathContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#step}.
*
* @param ctx the parse tree
*/
void enterStep(XpathParser.StepContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#step}.
*
* @param ctx the parse tree
*/
void exitStep(XpathParser.StepContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#axisSpecifier}.
*
* @param ctx the parse tree
*/
void enterAxisSpecifier(XpathParser.AxisSpecifierContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#axisSpecifier}.
*
* @param ctx the parse tree
*/
void exitAxisSpecifier(XpathParser.AxisSpecifierContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#nodeTest}.
*
* @param ctx the parse tree
*/
void enterNodeTest(XpathParser.NodeTestContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#nodeTest}.
*
* @param ctx the parse tree
*/
void exitNodeTest(XpathParser.NodeTestContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#predicate}.
*
* @param ctx the parse tree
*/
void enterPredicate(XpathParser.PredicateContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#predicate}.
*
* @param ctx the parse tree
*/
void exitPredicate(XpathParser.PredicateContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#abbreviatedStep}.
*
* @param ctx the parse tree
*/
void enterAbbreviatedStep(XpathParser.AbbreviatedStepContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#abbreviatedStep}.
*
* @param ctx the parse tree
*/
void exitAbbreviatedStep(XpathParser.AbbreviatedStepContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#expr}.
*
* @param ctx the parse tree
*/
void enterExpr(XpathParser.ExprContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#expr}.
*
* @param ctx the parse tree
*/
void exitExpr(XpathParser.ExprContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#primaryExpr}.
*
* @param ctx the parse tree
*/
void enterPrimaryExpr(XpathParser.PrimaryExprContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#primaryExpr}.
*
* @param ctx the parse tree
*/
void exitPrimaryExpr(XpathParser.PrimaryExprContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#functionCall}.
*
* @param ctx the parse tree
*/
void enterFunctionCall(XpathParser.FunctionCallContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#functionCall}.
*
* @param ctx the parse tree
*/
void exitFunctionCall(XpathParser.FunctionCallContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#unionExprNoRoot}.
*
* @param ctx the parse tree
*/
void enterUnionExprNoRoot(XpathParser.UnionExprNoRootContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#unionExprNoRoot}.
*
* @param ctx the parse tree
*/
void exitUnionExprNoRoot(XpathParser.UnionExprNoRootContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#pathExprNoRoot}.
*
* @param ctx the parse tree
*/
void enterPathExprNoRoot(XpathParser.PathExprNoRootContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#pathExprNoRoot}.
*
* @param ctx the parse tree
*/
void exitPathExprNoRoot(XpathParser.PathExprNoRootContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#filterExpr}.
*
* @param ctx the parse tree
*/
void enterFilterExpr(XpathParser.FilterExprContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#filterExpr}.
*
* @param ctx the parse tree
*/
void exitFilterExpr(XpathParser.FilterExprContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#orExpr}.
*
* @param ctx the parse tree
*/
void enterOrExpr(XpathParser.OrExprContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#orExpr}.
*
* @param ctx the parse tree
*/
void exitOrExpr(XpathParser.OrExprContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#andExpr}.
*
* @param ctx the parse tree
*/
void enterAndExpr(XpathParser.AndExprContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#andExpr}.
*
* @param ctx the parse tree
*/
void exitAndExpr(XpathParser.AndExprContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#equalityExpr}.
*
* @param ctx the parse tree
*/
void enterEqualityExpr(XpathParser.EqualityExprContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#equalityExpr}.
*
* @param ctx the parse tree
*/
void exitEqualityExpr(XpathParser.EqualityExprContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#relationalExpr}.
*
* @param ctx the parse tree
*/
void enterRelationalExpr(XpathParser.RelationalExprContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#relationalExpr}.
*
* @param ctx the parse tree
*/
void exitRelationalExpr(XpathParser.RelationalExprContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#additiveExpr}.
*
* @param ctx the parse tree
*/
void enterAdditiveExpr(XpathParser.AdditiveExprContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#additiveExpr}.
*
* @param ctx the parse tree
*/
void exitAdditiveExpr(XpathParser.AdditiveExprContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#multiplicativeExpr}.
*
* @param ctx the parse tree
*/
void enterMultiplicativeExpr(XpathParser.MultiplicativeExprContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#multiplicativeExpr}.
*
* @param ctx the parse tree
*/
void exitMultiplicativeExpr(XpathParser.MultiplicativeExprContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#unaryExprNoRoot}.
*
* @param ctx the parse tree
*/
void enterUnaryExprNoRoot(XpathParser.UnaryExprNoRootContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#unaryExprNoRoot}.
*
* @param ctx the parse tree
*/
void exitUnaryExprNoRoot(XpathParser.UnaryExprNoRootContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#qName}.
*
* @param ctx the parse tree
*/
void enterQName(XpathParser.QNameContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#qName}.
*
* @param ctx the parse tree
*/
void exitQName(XpathParser.QNameContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#functionName}.
*
* @param ctx the parse tree
*/
void enterFunctionName(XpathParser.FunctionNameContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#functionName}.
*
* @param ctx the parse tree
*/
void exitFunctionName(XpathParser.FunctionNameContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#variableReference}.
*
* @param ctx the parse tree
*/
void enterVariableReference(XpathParser.VariableReferenceContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#variableReference}.
*
* @param ctx the parse tree
*/
void exitVariableReference(XpathParser.VariableReferenceContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#nameTest}.
*
* @param ctx the parse tree
*/
void enterNameTest(XpathParser.NameTestContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#nameTest}.
*
* @param ctx the parse tree
*/
void exitNameTest(XpathParser.NameTestContext ctx);
/**
* Enter a parse tree produced by {@link XpathParser#nCName}.
*
* @param ctx the parse tree
*/
void enterNCName(XpathParser.NCNameContext ctx);
/**
* Exit a parse tree produced by {@link XpathParser#nCName}.
*
* @param ctx the parse tree
*/
void exitNCName(XpathParser.NCNameContext ctx);
}

@ -1,229 +0,0 @@
// Generated from resources/Xpath.g4 by ANTLR 4.7.2
package org.seimicrawler.xpath.antlr;
import org.antlr.v4.runtime.tree.ParseTreeVisitor;
/**
* This interface defines a complete generic visitor for a parse tree produced
* by {@link XpathParser}.
*
* @param <T> The return type of the visit operation. Use {@link Void} for
* operations with no return type.
*/
public interface XpathVisitor<T> extends ParseTreeVisitor<T> {
/**
* Visit a parse tree produced by {@link XpathParser#main}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitMain(XpathParser.MainContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#locationPath}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitLocationPath(XpathParser.LocationPathContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#absoluteLocationPathNoroot}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitAbsoluteLocationPathNoroot(XpathParser.AbsoluteLocationPathNorootContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#relativeLocationPath}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitRelativeLocationPath(XpathParser.RelativeLocationPathContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#step}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitStep(XpathParser.StepContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#axisSpecifier}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitAxisSpecifier(XpathParser.AxisSpecifierContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#nodeTest}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitNodeTest(XpathParser.NodeTestContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#predicate}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitPredicate(XpathParser.PredicateContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#abbreviatedStep}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitAbbreviatedStep(XpathParser.AbbreviatedStepContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#expr}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitExpr(XpathParser.ExprContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#primaryExpr}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitPrimaryExpr(XpathParser.PrimaryExprContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#functionCall}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitFunctionCall(XpathParser.FunctionCallContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#unionExprNoRoot}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitUnionExprNoRoot(XpathParser.UnionExprNoRootContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#pathExprNoRoot}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitPathExprNoRoot(XpathParser.PathExprNoRootContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#filterExpr}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitFilterExpr(XpathParser.FilterExprContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#orExpr}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitOrExpr(XpathParser.OrExprContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#andExpr}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitAndExpr(XpathParser.AndExprContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#equalityExpr}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitEqualityExpr(XpathParser.EqualityExprContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#relationalExpr}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitRelationalExpr(XpathParser.RelationalExprContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#additiveExpr}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitAdditiveExpr(XpathParser.AdditiveExprContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#multiplicativeExpr}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitMultiplicativeExpr(XpathParser.MultiplicativeExprContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#unaryExprNoRoot}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitUnaryExprNoRoot(XpathParser.UnaryExprNoRootContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#qName}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitQName(XpathParser.QNameContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#functionName}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitFunctionName(XpathParser.FunctionNameContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#variableReference}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitVariableReference(XpathParser.VariableReferenceContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#nameTest}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitNameTest(XpathParser.NameTestContext ctx);
/**
* Visit a parse tree produced by {@link XpathParser#nCName}.
*
* @param ctx the parse tree
* @return the visitor result
*/
T visitNCName(XpathParser.NCNameContext ctx);
}

@ -1,24 +0,0 @@
package org.seimicrawler.xpath.core;
import org.jsoup.select.Elements;
/**
* https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-AxisSpecifier
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/2/28.
*/
public interface AxisSelector {
/**
* assign name
*
* @return name
*/
String name();
/**
* @param context
* @return res
*/
XValue apply(Elements context);
}

@ -1,10 +0,0 @@
package org.seimicrawler.xpath.core;
/**
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2020/9/24.
*/
public class Constants {
public final static String DEF_TEXT_TAG_NAME = "JX_TEXT";
public final static String EL_SAME_TAG_INDEX_KEY = "EL_SAME_TAG_INDEX_KEY";
}

@ -1,13 +0,0 @@
package org.seimicrawler.xpath.core;
import java.util.List;
/**
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/2/28.
*/
public interface Function {
String name();
XValue call(Scope scope, List<XValue> params);
}

@ -1,21 +0,0 @@
package org.seimicrawler.xpath.core;
/**
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/2/28.
*/
public interface NodeTest {
/**
* 支持的函数名
*/
String name();
/**
* 函数具体逻辑
*
* @param scope 上下文
* @return 计算好的节点
*/
XValue call(Scope scope);
}

@ -1,76 +0,0 @@
package org.seimicrawler.xpath.core;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.seimicrawler.xpath.exception.XpathParserException;
/**
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/13.
*/
public class Scope {
private Elements context;
private boolean isRecursion = false;
private Scope parent;
private Scope(Elements context) {
super();
this.context = new Elements();
this.context.addAll(context);
}
private Scope(Element context) {
super();
this.context = new Elements();
this.context.add(context);
}
public static Scope create(Elements elements) {
return new Scope(elements);
}
public static Scope create(Element el) {
return new Scope(el);
}
public static Scope create(Scope scope) {
return new Scope(scope.context()).setParent(scope);
}
public Scope setParent(Scope scope) {
this.parent = scope;
return this;
}
public Scope getParent() {
return parent;
}
public void setContext(Elements context) {
this.context = context;
}
public boolean isRecursion() {
return isRecursion;
}
void recursion() {
this.isRecursion = true;
}
public void notRecursion() {
this.isRecursion = false;
}
public Elements context() {
return this.context;
}
public Element singleEl() {
if (context.size() == 1) {
return context.first();
}
throw new XpathParserException("current context is more than one el,total = " + context.size());
}
}

@ -1,226 +0,0 @@
package org.seimicrawler.xpath.core;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.seimicrawler.xpath.exception.XpathParserException;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.Date;
import java.util.List;
import java.util.Objects;
/**
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2017/12/5.
*/
public class XValue implements Comparable<XValue> {
public XValue(Object val) {
this.value = val;
}
public static XValue create(Object val) {
return new XValue(val);
}
private Object value;
private boolean isAttr = false;
private boolean isExprStr = false;
/**
* 同类型节点的顺序
*/
private int siblingIndex;
/**
* 满足特殊场景计算筛选需求
*/
private List<XValue> xValues;
public boolean isBoolean() {
return value instanceof Boolean;
}
public boolean isNumber() {
return value instanceof Number;
}
public boolean isElements() {
return value instanceof Elements;
}
public boolean isString() {
return value instanceof String;
}
public boolean isList() {
return value instanceof List;
}
public boolean isDate() {
return value instanceof Date;
}
public Boolean asBoolean() {
if (value instanceof Boolean) {
return (Boolean) value;
}
return value != null && !StringUtils.isBlank(asString());
}
public Date asDate() {
if (value instanceof String) {
try {
return DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.parse((String) value);
} catch (ParseException e) {
throw new XpathParserException("cast to date fail. vale = " + value);
}
}
if (value instanceof Date) {
return (Date) value;
}
throw new XpathParserException("cast to date fail. vale = " + value);
}
public Double asDouble() {
if (value instanceof String) {
return new BigDecimal((String) value).doubleValue();
} else if (value instanceof Number) {
return ((Number) value).doubleValue();
} else {
throw new XpathParserException("cast to number fail. vale = " + value);
}
}
public Long asLong() {
if (value instanceof String) {
//对于带小数点的,四舍五入
return new BigDecimal((String) value).setScale(0, BigDecimal.ROUND_HALF_UP).longValue();
} else if (value instanceof Number) {
return ((Number) value).longValue();
} else {
throw new XpathParserException("cast to number fail. vale = " + value);
}
}
public Elements asElements() {
return (Elements) value;
}
public String asString() {
if (isElements()) {
StringBuilder accum = new StringBuilder();
for (Element e : asElements()) {
accum.append(e.ownText());
}
return accum.toString();
} else if (value instanceof Element && Objects.equals(((Element) value).tagName(), Constants.DEF_TEXT_TAG_NAME)) {
return ((Element) value).ownText();
} else if (value instanceof List) {
return StringUtils.join((List) value, ",");
} else {
return String.valueOf(value).trim();
}
}
public List<String> asList() {
return (List<String>) value;
}
public XValue attr() {
this.isAttr = true;
return this;
}
public boolean isAttr() {
return isAttr;
}
public XValue exprStr() {
this.isExprStr = true;
String str = StringUtils.removeStart(String.valueOf(this.value), "'");
str = StringUtils.removeStart(str, "\"");
str = StringUtils.removeEnd(str, "'");
this.value = StringUtils.removeEnd(str, "\"");
return this;
}
public boolean isExprStr() {
return isExprStr;
}
public int getSiblingIndex() {
return siblingIndex;
}
public void setSiblingIndex(int siblingIndex) {
this.siblingIndex = siblingIndex;
}
public List<XValue> getxValues() {
return xValues;
}
public void setxValues(List<XValue> xValues) {
this.xValues = xValues;
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("value", value)
.append("isAttr", isAttr)
.append("isExprStr", isExprStr)
.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
XValue value1 = (XValue) o;
return Objects.equals(value, value1.value);
}
@Override
public int hashCode() {
return Objects.hashCode(value);
}
@Override
public int compareTo(XValue o) {
if (this.equals(o)) {
return 0;
}
if (o == null || o.value == null) {
return 1;
}
if (this.value == null) {
return -1;
}
if (isString()) {
return asString().compareTo(o.asString());
} else if (isNumber()) {
return asDouble().compareTo(o.asDouble());
} else {
throw new XpathParserException("Unsupported comparable XValue = " + toString());
}
}
public Class valType() {
if (value == null) {
return Object.class;
}
return value.getClass();
}
}

@ -1,613 +0,0 @@
package org.seimicrawler.xpath.core;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.seimicrawler.xpath.antlr.XpathBaseVisitor;
import org.seimicrawler.xpath.antlr.XpathParser;
import org.seimicrawler.xpath.exception.XpathMergeValueException;
import org.seimicrawler.xpath.exception.XpathParserException;
import org.seimicrawler.xpath.util.CommonUtil;
import org.seimicrawler.xpath.util.Scanner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.Stack;
import static org.seimicrawler.xpath.antlr.XpathParser.CONTAIN_WITH;
import static org.seimicrawler.xpath.antlr.XpathParser.DIVISION;
import static org.seimicrawler.xpath.antlr.XpathParser.END_WITH;
import static org.seimicrawler.xpath.antlr.XpathParser.GE;
import static org.seimicrawler.xpath.antlr.XpathParser.LE;
import static org.seimicrawler.xpath.antlr.XpathParser.LESS;
import static org.seimicrawler.xpath.antlr.XpathParser.MODULO;
import static org.seimicrawler.xpath.antlr.XpathParser.MORE_;
import static org.seimicrawler.xpath.antlr.XpathParser.MUL;
import static org.seimicrawler.xpath.antlr.XpathParser.REGEXP_NOT_WITH;
import static org.seimicrawler.xpath.antlr.XpathParser.REGEXP_WITH;
import static org.seimicrawler.xpath.antlr.XpathParser.START_WITH;
/**
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2017/8/30.
*/
public class XpathProcessor extends XpathBaseVisitor<XValue> {
private final Logger logger = LoggerFactory.getLogger(XpathProcessor.class);
private final Stack<Scope> scopeStack = new Stack<>();
private final Scope rootScope;
public XpathProcessor(Elements root) {
rootScope = Scope.create(root);
scopeStack.push(Scope.create(root).setParent(rootScope));
}
@Override
public XValue visitMain(XpathParser.MainContext ctx) {
return visit(ctx.expr());
}
@Override
public XValue visitLocationPath(XpathParser.LocationPathContext ctx) {
if (ctx.relativeLocationPath() != null && !ctx.relativeLocationPath().isEmpty()) {
return visit(ctx.relativeLocationPath());
}
return visit(ctx.absoluteLocationPathNoroot());
}
@Override
public XValue visitAbsoluteLocationPathNoroot(XpathParser.AbsoluteLocationPathNorootContext ctx) {
// '//'
if (Objects.equals(ctx.op.getText(), "//")) {
currentScope().recursion();
}
return visit(ctx.relativeLocationPath());
}
@Override
public XValue visitRelativeLocationPath(XpathParser.RelativeLocationPathContext ctx) {
XValue finalVal = null;
for (int i = 0; i < ctx.getChildCount(); i++) {
ParseTree step = ctx.getChild(i);
if (step instanceof XpathParser.StepContext) {
finalVal = visit(step);
if (finalVal.isElements()) {
updateCurrentContext(finalVal.asElements());
}
} else {
if ("//".equals(step.getText())) {
currentScope().recursion();
} else {
currentScope().notRecursion();
}
}
}
return finalVal;
}
@Override
public XValue visitStep(XpathParser.StepContext ctx) {
if (ctx.abbreviatedStep() != null && !ctx.abbreviatedStep().isEmpty()) {
return visit(ctx.abbreviatedStep());
}
boolean filterByAttr = false;
boolean isAxisOk = false;
if (ctx.axisSpecifier() != null && !ctx.axisSpecifier().isEmpty()) {
XValue axis = visit(ctx.axisSpecifier());
if (axis != null) {
isAxisOk = true;
if (axis.isElements()) {
updateCurrentContext(axis.asElements());
} else if (axis.isAttr()) {
filterByAttr = true;
}
}
}
if (ctx.nodeTest() != null && !ctx.nodeTest().isEmpty()) {
XValue nodeTest = visit(ctx.nodeTest());
if (filterByAttr) {
Elements context = currentScope().context();
String attrName = nodeTest.asString();
if (currentScope().isRecursion()) {
if (context.size() == 1) {
Element el = currentScope().singleEl();
Elements findRes = el.select("[" + attrName + "]");
List<String> attrs = new LinkedList<>();
for (Element e : findRes) {
attrs.add(e.attr(attrName));
}
return XValue.create(attrs);
} else {
Elements findRes = new Elements();
for (Element el : context) {
findRes.addAll(el.select("[" + attrName + "]"));
}
List<String> attrs = new LinkedList<>();
for (Element e : findRes) {
attrs.add(e.attr(attrName));
}
return XValue.create(attrs);
}
} else {
if (context.size() == 1) {
Element el = currentScope().singleEl();
return XValue.create(el.attr(attrName));
} else {
List<String> attrs = new LinkedList<>();
for (Element el : context) {
attrs.add(el.attr(attrName));
}
return XValue.create(attrs);
}
}
} else {
if (nodeTest.isExprStr()) {
String tagName = nodeTest.asString();
Elements current = currentScope().context();
if (currentScope().isRecursion()) {
updateCurrentContext(current.select(tagName));
} else {
Elements newContext = new Elements();
for (Element el : currentScope().context()) {
if (isAxisOk) {
if (el.nodeName().equals(tagName) || "*".equals(tagName)) {
newContext.add(el);
}
} else {
for (Element e : el.children()) {
if (e.nodeName().equals(tagName) || "*".equals(tagName)) {
newContext.add(e);
}
}
}
}
updateCurrentContext(newContext);
}
} else {
// nodeType 计算结果,直接返给上层
if (nodeTest.isElements()) {
updateCurrentContext(nodeTest.asElements());
} else {
return nodeTest;
}
}
}
}
if (ctx.predicate() != null && ctx.predicate().size() > 0) {
for (XpathParser.PredicateContext predicate : ctx.predicate()) {
XValue predicateVal = visit(predicate);
updateCurrentContext(predicateVal.asElements());
}
}
return XValue.create(currentScope().context());
}
@Override
public XValue visitAbbreviatedStep(XpathParser.AbbreviatedStepContext ctx) {
if ("..".equals(ctx.getText())) {
Set<Element> total = new HashSet<>();
Elements newContext = new Elements();
for (Element e : currentScope().context()) {
total.add(e.parent());
}
newContext.addAll(total);
return XValue.create(newContext);
} else {
return XValue.create(currentScope().context());
}
}
@Override
public XValue visitAxisSpecifier(XpathParser.AxisSpecifierContext ctx) {
TerminalNode axisNode = ctx.AxisName();
if (axisNode != null) {
String axis = ctx.AxisName().getText();
AxisSelector axisSelector = Scanner.findSelectorByName(axis);
return axisSelector.apply(currentScope().context());
} else {
String token = ctx.getText();
if ("@".equals(token)) {
return XValue.create(null).attr();
}
}
return null;
}
@Override
public XValue visitNodeTest(XpathParser.NodeTestContext ctx) {
if (ctx.nameTest() != null) {
return visit(ctx.nameTest());
}
if (ctx.NodeType() != null) {
NodeTest nodeTest = Scanner.findNodeTestByName(ctx.NodeType().getText());
return nodeTest.call(currentScope());
}
// todo: | 'processing-instruction' '(' Literal ')'
return null;
}
@Override
public XValue visitPredicate(XpathParser.PredicateContext ctx) {
Elements newContext = new Elements();
for (Element e : currentScope().context()) {
scopeStack.push(Scope.create(e).setParent(currentScope()));
XValue exprVal = visit(ctx.expr());
scopeStack.pop();
if (exprVal.isNumber()) {
long index = exprVal.asLong();
if (index < 0) {
if (Objects.equals(e.tagName(), Constants.DEF_TEXT_TAG_NAME)) {
index = CommonUtil.getJxSameTagIndexInSiblings(e) + index + 1;
} else {
index = CommonUtil.sameTagElNums(e, currentScope()) + index + 1;
}
if (index < 0) {
index = 1;
}
}
if (Objects.equals(e.tagName(), Constants.DEF_TEXT_TAG_NAME)) {
if (index == CommonUtil.getJxSameTagIndexInSiblings(e)) {
newContext.add(e);
}
} else {
if (index == CommonUtil.getElIndexInSameTags(e, currentScope())) {
newContext.add(e);
}
}
} else if (exprVal.isBoolean()) {
if (exprVal.asBoolean()) {
newContext.add(e);
}
} else if (exprVal.isString()) {
//根据表达式执行结果是否为空作为条件,如 //*[@foo]
if (StringUtils.isNotBlank(exprVal.asString())) {
newContext.add(e);
}
} else if (exprVal.isElements()) {
//根据表达式执行结果是否为空进行过滤,如 //div[./a]
Elements els = exprVal.asElements();
if (els.size() > 0) {
newContext.add(e);
}
} else if (exprVal.isList()) {
//根据表达式执行结果是否为空进行过滤,如 //div[./a/text()]
List<String> stringList = exprVal.asList();
if (stringList.size() > 0) {
newContext.add(e);
}
} else {
throw new XpathParserException("unknown expr val:" + exprVal);
}
}
return XValue.create(newContext);
}
@Override
public XValue visitNameTest(XpathParser.NameTestContext ctx) {
if ("*".equals(ctx.getText())) {
return XValue.create("*").exprStr();
} else if (ctx.qName() != null && !ctx.qName().isEmpty()) {
return visit(ctx.qName());
} else if (ctx.nCName() != null && !ctx.nCName().isEmpty()) {
return visit(ctx.nCName());
}
return null;
}
@Override
public XValue visitQName(XpathParser.QNameContext ctx) {
List<XpathParser.NCNameContext> ncNameContexts = ctx.nCName();
if (ncNameContexts != null) {
if (ncNameContexts.size() > 1) {
List<String> ncNames = new LinkedList<>();
for (XpathParser.NCNameContext ncNameContext : ncNameContexts) {
XValue value = visit(ncNameContext);
if (value != null) {
ncNames.add(value.asString());
}
}
// TODO: 2018/3/14 考虑体现出这个结构来
return XValue.create(StringUtils.join(ncNames, ":"));
} else {
return visit(ncNameContexts.get(0));
}
}
return null;
}
@Override
public XValue visitNCName(XpathParser.NCNameContext ctx) {
if (ctx.AxisName() != null) {
return XValue.create(ctx.AxisName().getText()).exprStr();
} else {
return XValue.create(ctx.NCName().getText()).exprStr();
}
}
@Override
public XValue visitExpr(XpathParser.ExprContext ctx) {
return visit(ctx.orExpr());
}
@Override
public XValue visitOrExpr(XpathParser.OrExprContext ctx) {
List<XpathParser.AndExprContext> andExprContexts = ctx.andExpr();
if (andExprContexts.size() > 1) {
Boolean res = visit(andExprContexts.get(0)).asBoolean();
for (int i = 1; i < andExprContexts.size(); i++) {
res = (res | visit(andExprContexts.get(i)).asBoolean());
}
return XValue.create(res);
} else {
return visit(andExprContexts.get(0));
}
}
@Override
public XValue visitAndExpr(XpathParser.AndExprContext ctx) {
List<XpathParser.EqualityExprContext> equalityExprContexts = ctx.equalityExpr();
if (equalityExprContexts.size() > 1) {
Boolean res = visit(equalityExprContexts.get(0)).asBoolean();
for (int i = 1; i < equalityExprContexts.size(); i++) {
res = (res & visit(equalityExprContexts.get(i)).asBoolean());
}
return XValue.create(res);
} else {
return visit(equalityExprContexts.get(0));
}
}
@Override
public XValue visitEqualityExpr(XpathParser.EqualityExprContext ctx) {
List<XpathParser.RelationalExprContext> relationalExprContexts = ctx.relationalExpr();
if (relationalExprContexts.size() == 1) {
return visit(relationalExprContexts.get(0));
} else if (relationalExprContexts.size() == 2) {
XValue left = visit(relationalExprContexts.get(0));
XValue right = visit(relationalExprContexts.get(1));
if ("=".equals(ctx.op.getText())) {
if (left.valType().equals(right.valType())) {
return XValue.create(Objects.equals(left, right));
} else {
return XValue.create(Objects.equals(left.asString(), right.asString()));
}
} else {
if (left.valType().equals(right.valType())) {
return XValue.create(!Objects.equals(left, right));
} else {
return XValue.create(!Objects.equals(left.asString(), right.asString()));
}
}
} else {
throw new XpathParserException("error equalityExpr near:" + ctx.getText());
}
}
@Override
public XValue visitRelationalExpr(XpathParser.RelationalExprContext ctx) {
List<XpathParser.AdditiveExprContext> additiveExprContexts = ctx.additiveExpr();
if (additiveExprContexts.size() == 1) {
return visit(additiveExprContexts.get(0));
} else if (additiveExprContexts.size() == 2) {
XValue left = visit(additiveExprContexts.get(0));
XValue right = visit(additiveExprContexts.get(1));
switch (ctx.op.getType()) {
case MORE_:
return XValue.create(left.compareTo(right) > 0);
case GE:
return XValue.create(left.compareTo(right) >= 0);
case LESS:
return XValue.create(left.compareTo(right) < 0);
case LE:
return XValue.create(left.compareTo(right) <= 0);
case START_WITH:
return XValue.create(left.asString().startsWith(right.asString()));
case END_WITH:
return XValue.create(left.asString().endsWith(right.asString()));
case CONTAIN_WITH:
return XValue.create(left.asString().contains(right.asString()));
case REGEXP_WITH:
return XValue.create(left.asString().matches(right.asString()));
case REGEXP_NOT_WITH:
return XValue.create(!left.asString().matches(right.asString()));
default:
throw new XpathParserException("unknown operator" + ctx.op.getText());
}
} else {
throw new XpathParserException("error equalityExpr near:" + ctx.getText());
}
}
@Override
public XValue visitAdditiveExpr(XpathParser.AdditiveExprContext ctx) {
List<XpathParser.MultiplicativeExprContext> multiplicativeExprContexts = ctx.multiplicativeExpr();
if (multiplicativeExprContexts.size() == 1) {
return visit(multiplicativeExprContexts.get(0));
} else {
Double res = visit(multiplicativeExprContexts.get(0)).asDouble();
String op = null;
for (int i = 1; i < ctx.getChildCount(); i++) {
ParseTree chiCtx = ctx.getChild(i);
if (chiCtx instanceof XpathParser.MultiplicativeExprContext) {
XValue next = visit(chiCtx);
if ("+".equals(op)) {
res += next.asDouble();
} else if ("-".equals(op)) {
res -= next.asDouble();
} else {
throw new XpathParserException("syntax error, " + ctx.getText());
}
} else {
op = chiCtx.getText();
}
}
return XValue.create(res);
}
}
@Override
public XValue visitMultiplicativeExpr(XpathParser.MultiplicativeExprContext ctx) {
if (ctx.multiplicativeExpr() == null || ctx.multiplicativeExpr().isEmpty()) {
return visit(ctx.unaryExprNoRoot());
} else {
XValue left = visit(ctx.unaryExprNoRoot());
XValue right = visit(ctx.multiplicativeExpr());
switch (ctx.op.getType()) {
case MUL:
return XValue.create(left.asDouble() * right.asDouble());
case DIVISION:
return XValue.create(left.asDouble() / right.asDouble());
case MODULO:
return XValue.create(left.asDouble() % right.asDouble());
default:
throw new XpathParserException("syntax error, " + ctx.getText());
}
}
}
@Override
public XValue visitUnaryExprNoRoot(XpathParser.UnaryExprNoRootContext ctx) {
XValue value = visit(ctx.unionExprNoRoot());
if (ctx.sign == null) {
return value;
}
return XValue.create(-value.asDouble());
}
@Override
public XValue visitUnionExprNoRoot(XpathParser.UnionExprNoRootContext ctx) {
if (ctx.pathExprNoRoot() == null && !ctx.pathExprNoRoot().isEmpty()) {
return visit(ctx.unionExprNoRoot());
}
XValue pathExprNoRoot = visit(ctx.pathExprNoRoot());
if (ctx.op == null) {
return pathExprNoRoot;
}
scopeStack.push(Scope.create(currentScope().getParent()));
XValue unionExprNoRoot = visit(ctx.unionExprNoRoot());
scopeStack.pop();
if (pathExprNoRoot.isElements()) {
if (unionExprNoRoot.isElements()) {
pathExprNoRoot.asElements().addAll(unionExprNoRoot.asElements());
} else {
Element element = new Element("V");
element.appendText(unionExprNoRoot.asString());
pathExprNoRoot.asElements().add(element);
}
return pathExprNoRoot;
} else if (pathExprNoRoot.isString()) {
if (unionExprNoRoot.isElements()) {
Element element = new Element("V");
element.appendText(pathExprNoRoot.asString());
unionExprNoRoot.asElements().add(element);
return unionExprNoRoot;
} else {
return XValue.create(pathExprNoRoot.asString() + unionExprNoRoot.asString());
}
} else if (pathExprNoRoot.isBoolean()) {
if (unionExprNoRoot.isBoolean()) {
return XValue.create(pathExprNoRoot.asBoolean() | unionExprNoRoot.asBoolean());
} else if (unionExprNoRoot.isElements()) {
Element element = new Element("V");
element.appendText(pathExprNoRoot.asString());
unionExprNoRoot.asElements().add(element);
return unionExprNoRoot;
} else if (unionExprNoRoot.isString()) {
return XValue.create(pathExprNoRoot.asBoolean() + unionExprNoRoot.asString());
} else {
throw new XpathMergeValueException("can not merge val1=" + pathExprNoRoot.asBoolean() + ",val2=" + unionExprNoRoot.asString());
}
} else if (pathExprNoRoot.isNumber()) {
if (unionExprNoRoot.isString()) {
return XValue.create(pathExprNoRoot.asDouble() + unionExprNoRoot.asString());
} else if (unionExprNoRoot.isElements()) {
Element element = new Element("V");
element.appendText(pathExprNoRoot.asString());
unionExprNoRoot.asElements().add(element);
return unionExprNoRoot;
} else {
throw new XpathMergeValueException("can not merge val1=" + pathExprNoRoot.asDouble() + ",val2=" + unionExprNoRoot.asString());
}
} else {
List<String> tmpVal = new LinkedList<>();
if (StringUtils.isNotBlank(pathExprNoRoot.asString())) {
tmpVal.add(pathExprNoRoot.asString());
}
if (StringUtils.isNotBlank(unionExprNoRoot.asString())) {
tmpVal.add(unionExprNoRoot.asString());
}
return XValue.create(StringUtils.join(tmpVal, ","));
}
}
@Override
public XValue visitPathExprNoRoot(XpathParser.PathExprNoRootContext ctx) {
if (ctx.locationPath() != null && !ctx.locationPath().isEmpty()) {
return visit(ctx.locationPath());
}
if (ctx.op == null) {
return visit(ctx.filterExpr());
}
if ("//".equals(ctx.op.getText())) {
currentScope().recursion();
}
return visit(ctx.relativeLocationPath());
}
@Override
public XValue visitFilterExpr(XpathParser.FilterExprContext ctx) {
//暂时先不考虑支持 : primaryExpr predicate*
return visit(ctx.primaryExpr());
}
@Override
public XValue visitPrimaryExpr(XpathParser.PrimaryExprContext ctx) {
if (ctx.expr() != null && !ctx.expr().isEmpty()) {
return visit(ctx.expr());
} else if (ctx.functionCall() != null && !ctx.functionCall().isEmpty()) {
return visit(ctx.functionCall());
} else if (ctx.Literal() != null) {
return XValue.create(ctx.Literal().getText()).exprStr();
} else if (ctx.Number() != null) {
return XValue.create(NumberUtils.createDouble(ctx.Number().getText()));
}
throw new XpathParserException("not support variableReference:" + ctx.getText());
}
@Override
public XValue visitFunctionCall(XpathParser.FunctionCallContext ctx) {
List<XValue> params = new LinkedList<>();
XValue funcName = visit(ctx.functionName());
for (XpathParser.ExprContext exprContext : ctx.expr()) {
scopeStack.push(Scope.create(currentScope()));
params.add(visit(exprContext));
scopeStack.pop();
}
Function function = Scanner.findFunctionByName(funcName.asString());
return function.call(currentScope(), params);
}
@Override
public XValue visitFunctionName(XpathParser.FunctionNameContext ctx) {
return visit(ctx.qName());
}
private Scope currentScope() {
return scopeStack.peek();
}
private void updateCurrentContext(Elements newContext) {
scopeStack.peek().setContext(newContext);
}
}

@ -1,34 +0,0 @@
package org.seimicrawler.xpath.core.axis;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.seimicrawler.xpath.core.AxisSelector;
import org.seimicrawler.xpath.core.XValue;
import java.util.LinkedList;
import java.util.List;
/**
* the ancestor-or-self axis contains the context node and the ancestors of the context node;
* thus, the ancestor axis will always include the root node
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/26.
*/
public class AncestorOrSelfSelector implements AxisSelector {
@Override
public String name() {
return "ancestor-or-self";
}
@Override
public XValue apply(Elements context) {
List<Element> total = new LinkedList<>();
for (Element el : context) {
total.addAll(el.parents());
//include self
total.add(el);
}
return XValue.create(new Elements(total));
}
}

@ -1,33 +0,0 @@
package org.seimicrawler.xpath.core.axis;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.seimicrawler.xpath.core.AxisSelector;
import org.seimicrawler.xpath.core.XValue;
import java.util.LinkedList;
import java.util.List;
/**
* the ancestor axis contains the ancestors of the context node; the ancestors of the context node consist of
* the parent of context node and the parent's parent and so on; thus, the ancestor axis will always include
* the root node, unless the context node is the root node
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/26.
*/
public class AncestorSelector implements AxisSelector {
@Override
public String name() {
return "ancestor";
}
@Override
public XValue apply(Elements context) {
List<Element> total = new LinkedList<>();
for (Element el : context) {
total.addAll(el.parents());
}
return XValue.create(new Elements(total));
}
}

@ -1,32 +0,0 @@
package org.seimicrawler.xpath.core.axis;
import org.jsoup.select.Elements;
import org.seimicrawler.xpath.core.AxisSelector;
import org.seimicrawler.xpath.core.XValue;
/**
* the attribute axis contains the attributes of the context node; the axis will be empty unless the context node is an element
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/26.
*/
public class AttributeSelector implements AxisSelector {
/**
* assign name
*
* @return name
*/
@Override
public String name() {
return "attribute";
}
/**
* @param context
* @return res
*/
@Override
public XValue apply(Elements context) {
return XValue.create(null).attr();
}
}

@ -1,28 +0,0 @@
package org.seimicrawler.xpath.core.axis;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.seimicrawler.xpath.core.AxisSelector;
import org.seimicrawler.xpath.core.XValue;
/**
* the child axis contains the children of the context node
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/26.
*/
public class ChildSelector implements AxisSelector {
@Override
public String name() {
return "child";
}
@Override
public XValue apply(Elements context) {
Elements childs = new Elements();
for (Element el : context) {
childs.addAll(el.children());
}
return XValue.create(childs);
}
}

@ -1,34 +0,0 @@
package org.seimicrawler.xpath.core.axis;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.seimicrawler.xpath.core.AxisSelector;
import org.seimicrawler.xpath.core.XValue;
import java.util.HashSet;
import java.util.Set;
/**
* the descendant-or-self axis contains the context node and the descendants of the context node
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/26.
*/
public class DescendantOrSelfSelector implements AxisSelector {
@Override
public String name() {
return "descendant-or-self";
}
@Override
public XValue apply(Elements context) {
Set<Element> total = new HashSet<>();
Elements descendant = new Elements();
for (Element el : context) {
Elements tmp = el.getAllElements();
total.addAll(tmp);
}
descendant.addAll(total);
return XValue.create(descendant);
}
}

@ -1,37 +0,0 @@
package org.seimicrawler.xpath.core.axis;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.seimicrawler.xpath.core.AxisSelector;
import org.seimicrawler.xpath.core.XValue;
import java.util.HashSet;
import java.util.Set;
/**
* the descendant axis contains the descendants of the context node; a descendant is a child or a child
* of a child and so on; thus the descendant axis never contains attribute or namespace nodes
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/26.
*/
public class DescendantSelector implements AxisSelector {
@Override
public String name() {
return "descendant";
}
@Override
public XValue apply(Elements context) {
Set<Element> total = new HashSet<>();
Elements descendant = new Elements();
for (Element el : context) {
Elements tmp = el.getAllElements();
//exclude self
tmp.remove(el);
total.addAll(tmp);
}
descendant.addAll(total);
return XValue.create(descendant);
}
}

@ -1,51 +0,0 @@
package org.seimicrawler.xpath.core.axis;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.seimicrawler.xpath.core.AxisSelector;
import org.seimicrawler.xpath.core.XValue;
import org.seimicrawler.xpath.util.CommonUtil;
import java.util.LinkedList;
import java.util.List;
/**
* the following axis contains all nodes in the same document as the context node that are after the context node in
* document order, excluding any descendants and excluding attribute nodes and namespace nodes
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/26.
*/
public class FollowingSelector implements AxisSelector {
@Override
public String name() {
return "following";
}
@Override
public XValue apply(Elements context) {
List<Element> total = new LinkedList<>();
for (Element el : context) {
Elements p = el.parents();
for (Element pe : p) {
Elements fs = CommonUtil.followingSibling(pe);
if (fs == null) {
continue;
}
for (Element pse : fs) {
//include pse
total.addAll(pse.getAllElements());
}
}
Elements fs = CommonUtil.followingSibling(el);
if (fs == null) {
continue;
}
for (Element se : fs) {
total.addAll(se.getAllElements());
}
}
return XValue.create(new Elements(total));
}
}

@ -1,44 +0,0 @@
package org.seimicrawler.xpath.core.axis;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.seimicrawler.xpath.core.AxisSelector;
import org.seimicrawler.xpath.core.XValue;
import java.util.LinkedList;
import java.util.List;
/**
* the following-sibling-one JsoupXpath自定义扩展,比较常用
*
* @author https://github.com/hermitmmll
* @since 2018/3/27.
*/
public class FollowingSiblingOneSelector implements AxisSelector {
/**
* assign name
*
* @return name
*/
@Override
public String name() {
return "following-sibling-one";
}
/**
* @param context
* @return res
*/
@Override
public XValue apply(Elements context) {
List<Element> total = new LinkedList<>();
for (Element el : context) {
if (el.nextElementSibling() != null) {
total.add(el.nextElementSibling());
}
}
Elements newContext = new Elements();
newContext.addAll(total);
return XValue.create(newContext);
}
}

@ -1,48 +0,0 @@
package org.seimicrawler.xpath.core.axis;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.seimicrawler.xpath.core.AxisSelector;
import org.seimicrawler.xpath.core.XValue;
import org.seimicrawler.xpath.util.CommonUtil;
import java.util.LinkedList;
import java.util.List;
/**
* the following-sibling axis contains all the following siblings of the context node; if the context node is an
* attribute node or namespace node, the following-sibling axis is empty
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/27.
*/
public class FollowingSiblingSelector implements AxisSelector {
/**
* assign name
*
* @return name
*/
@Override
public String name() {
return "following-sibling";
}
/**
* @param context
* @return res
*/
@Override
public XValue apply(Elements context) {
List<Element> total = new LinkedList<>();
for (Element el : context) {
Elements fs = CommonUtil.followingSibling(el);
if (fs == null) {
continue;
}
total.addAll(fs);
}
Elements newContext = new Elements();
newContext.addAll(total);
return XValue.create(newContext);
}
}

@ -1,34 +0,0 @@
package org.seimicrawler.xpath.core.axis;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.seimicrawler.xpath.core.AxisSelector;
import org.seimicrawler.xpath.core.XValue;
import java.util.LinkedList;
import java.util.List;
/**
* the parent axis contains the parent of the context node, if there is one
* https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-AxisSpecifier
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/2/28.
*/
public class ParentSelector implements AxisSelector {
@Override
public String name() {
return "parent";
}
@Override
public XValue apply(Elements context) {
List<Element> total = new LinkedList<>();
Elements parents = new Elements();
for (Element el : context) {
total.add(el.parent());
}
parents.addAll(total);
return XValue.create(parents);
}
}

@ -1,56 +0,0 @@
package org.seimicrawler.xpath.core.axis;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.seimicrawler.xpath.core.AxisSelector;
import org.seimicrawler.xpath.core.XValue;
import org.seimicrawler.xpath.util.CommonUtil;
import java.util.LinkedList;
import java.util.List;
/**
* the preceding axis contains all nodes in the same document as the context node that are before the context
* node in document order, excluding any ancestors and excluding attribute nodes and namespace nodes
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/27.
*/
public class PrecedingSelector implements AxisSelector {
/**
* assign name
*
* @return name
*/
@Override
public String name() {
return "preceding";
}
/**
* @param context
* @return res
*/
@Override
public XValue apply(Elements context) {
Elements preceding = new Elements();
List<Element> total = new LinkedList<>();
for (Element el : context) {
Elements p = el.parents();
for (Element pe : p) {
Elements ps = CommonUtil.precedingSibling(pe);
if (ps == null) {
continue;
}
total.addAll(ps);
}
Elements ps = CommonUtil.precedingSibling(el);
if (ps == null) {
continue;
}
total.addAll(ps);
}
preceding.addAll(total);
return XValue.create(preceding);
}
}

@ -1,44 +0,0 @@
package org.seimicrawler.xpath.core.axis;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.seimicrawler.xpath.core.AxisSelector;
import org.seimicrawler.xpath.core.XValue;
import java.util.LinkedList;
import java.util.List;
/**
* the preceding-sibling-one axis JsoupXpath自定义扩展用于选取节点的前一个兄弟节点如果存在的话
*
* @author github.com/hermitmmll
* @since 2018/3/27.
*/
public class PrecedingSiblingOneSelector implements AxisSelector {
/**
* assign name
*
* @return name
*/
@Override
public String name() {
return "preceding-sibling-one";
}
/**
* @param context
* @return res
*/
@Override
public XValue apply(Elements context) {
List<Element> total = new LinkedList<>();
for (Element el : context) {
if (el.previousElementSibling() != null) {
total.add(el);
}
}
Elements newContext = new Elements();
newContext.addAll(total);
return XValue.create(newContext);
}
}

@ -1,48 +0,0 @@
package org.seimicrawler.xpath.core.axis;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.seimicrawler.xpath.core.AxisSelector;
import org.seimicrawler.xpath.core.XValue;
import org.seimicrawler.xpath.util.CommonUtil;
import java.util.LinkedList;
import java.util.List;
/**
* the preceding-sibling axis contains all the preceding siblings of the context node; if the context node is
* an attribute node or namespace node, the preceding-sibling axis is empty
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/27.
*/
public class PrecedingSiblingSelector implements AxisSelector {
/**
* assign name
*
* @return name
*/
@Override
public String name() {
return "preceding-sibling";
}
/**
* @param context
* @return res
*/
@Override
public XValue apply(Elements context) {
List<Element> total = new LinkedList<>();
for (Element el : context) {
Elements ps = CommonUtil.precedingSibling(el);
if (ps == null) {
continue;
}
total.addAll(ps);
}
Elements newContext = new Elements();
newContext.addAll(total);
return XValue.create(newContext);
}
}

@ -1,23 +0,0 @@
package org.seimicrawler.xpath.core.axis;
import org.jsoup.select.Elements;
import org.seimicrawler.xpath.core.AxisSelector;
import org.seimicrawler.xpath.core.XValue;
/**
* the self axis contains just the context node itself
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/2/28.
*/
public class SelfSelector implements AxisSelector {
@Override
public String name() {
return "self";
}
@Override
public XValue apply(Elements es) {
return XValue.create(es);
}
}

@ -1,30 +0,0 @@
package org.seimicrawler.xpath.core.function;
import org.seimicrawler.xpath.core.Function;
import org.seimicrawler.xpath.core.Scope;
import org.seimicrawler.xpath.core.XValue;
import java.util.List;
/**
* Function: string concat(string, string, string*)
* The concat function returns the concatenation of its arguments.
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/26.
*/
public class Concat implements Function {
@Override
public String name() {
return "concat";
}
@Override
public XValue call(Scope scope, List<XValue> params) {
StringBuilder accum = new StringBuilder();
for (XValue v : params) {
accum.append(v.asString());
}
return XValue.create(accum.toString());
}
}

@ -1,29 +0,0 @@
package org.seimicrawler.xpath.core.function;
import org.seimicrawler.xpath.core.Function;
import org.seimicrawler.xpath.core.Scope;
import org.seimicrawler.xpath.core.XValue;
import java.util.List;
/**
* Function: boolean contains(string, string)
* <p>
* The contains function returns true if the first argument string contains the second argument string, and otherwise returns false.
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/26.
*/
public class Contains implements Function {
@Override
public String name() {
return "contains";
}
@Override
public XValue call(Scope scope, List<XValue> params) {
String first = params.get(0).asString();
String second = params.get(1).asString();
return XValue.create(first.contains(second));
}
}

@ -1,29 +0,0 @@
package org.seimicrawler.xpath.core.function;
import org.seimicrawler.xpath.core.Function;
import org.seimicrawler.xpath.core.Scope;
import org.seimicrawler.xpath.core.XValue;
import java.util.List;
/**
* Function: number count(node-set)
* The count function returns the number of nodes in the argument node-set.
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/27.
*/
public class Count implements Function {
@Override
public String name() {
return "count";
}
@Override
public XValue call(Scope scope, List<XValue> params) {
if (params == null || params.size() == 0) {
return XValue.create(0);
}
return XValue.create(params.get(0).asElements().size());
}
}

@ -1,25 +0,0 @@
package org.seimicrawler.xpath.core.function;
import org.seimicrawler.xpath.core.Function;
import org.seimicrawler.xpath.core.Scope;
import org.seimicrawler.xpath.core.XValue;
import java.util.List;
/**
* first in xpath is 1
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/29.
*/
public class First implements Function {
@Override
public String name() {
return "first";
}
@Override
public XValue call(Scope scope, List<XValue> params) {
return XValue.create(1);
}
}

@ -1,47 +0,0 @@
package org.seimicrawler.xpath.core.function;
import org.apache.commons.lang3.time.FastDateFormat;
import org.seimicrawler.xpath.core.Function;
import org.seimicrawler.xpath.core.Scope;
import org.seimicrawler.xpath.core.XValue;
import org.seimicrawler.xpath.exception.XpathParserException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Locale;
/**
* Function: string format-date(string, string, string)
* The format-date function returns Date object
* The first parameter is the date and time
* The second parameter is the time format of the first parameter.
* The third parameter is not required, and is required if the date format of the first parameter requires that the time zone must be specified
* For example, format-date("1999/04/01","yyyy/MM/dd") returns Date Object, and format-date("1999/04/01 07:55:23 pm","yyyy/MM/dd hh:mm:ss a",'en') returns Date Object.
*
* @author github.com/zzldn@163.com
* @since 2019/1/22.
*/
public class FormatDate implements Function {
@Override
public String name() {
return "format-date";
}
@Override
public XValue call(Scope scope, List<XValue> params) {
String value = params.get(0).asString();
String patten = params.get(1).asString();
try {
if (params.size() > 2 && null != params.get(2)) {
final Locale locale = Locale.forLanguageTag(params.get(2).asString());
final SimpleDateFormat format = new SimpleDateFormat(patten, locale);
return XValue.create(format.parse(value));
}
return XValue.create(FastDateFormat.getInstance(patten).parse(value));
} catch (ParseException e) {
throw new XpathParserException("date format exception!", e);
}
}
}

@ -1,28 +0,0 @@
package org.seimicrawler.xpath.core.function;
import org.seimicrawler.xpath.core.Function;
import org.seimicrawler.xpath.core.Scope;
import org.seimicrawler.xpath.core.XValue;
import java.util.List;
/**
* Function: number last()
* The last function returns a number equal to the context size from the expression evaluation context.
* e.g.
* para[last()] selects the last para child of the context node
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/27.
*/
public class Last implements Function {
@Override
public String name() {
return "last";
}
@Override
public XValue call(Scope scope, List<XValue> params) {
return XValue.create(-1);
}
}

@ -1,30 +0,0 @@
package org.seimicrawler.xpath.core.function;
import org.seimicrawler.xpath.core.Function;
import org.seimicrawler.xpath.core.Scope;
import org.seimicrawler.xpath.core.XValue;
import org.seimicrawler.xpath.exception.XpathParserException;
import java.util.List;
/**
* bool not(bool)
*
* @author github.com/hermitmmll
* @since 2018/4/3.
*/
public class Not implements Function {
@Override
public String name() {
return "not";
}
@Override
public XValue call(Scope scope, List<XValue> params) {
if (params.size() == 1) {
return XValue.create(!params.get(0).asBoolean());
} else {
throw new XpathParserException("error param in not(bool) function.Please check.");
}
}
}

@ -1,28 +0,0 @@
package org.seimicrawler.xpath.core.function;
import org.seimicrawler.xpath.core.Function;
import org.seimicrawler.xpath.core.Scope;
import org.seimicrawler.xpath.core.XValue;
import org.seimicrawler.xpath.util.CommonUtil;
import java.util.List;
/**
* The position function returns a number equal to the context position from the expression evaluation context.
* e.g.
* /child::doc/child::chapter[position()=5]/child::section[position()=2] selects the second section of the fifth chapter of the doc document element
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/2/28.
*/
public class Position implements Function {
@Override
public String name() {
return "position";
}
@Override
public XValue call(Scope scope, List<XValue> params) {
return XValue.create(CommonUtil.getElIndexInSameTags(scope.singleEl(), scope.getParent()));
}
}

@ -1,29 +0,0 @@
package org.seimicrawler.xpath.core.function;
import org.seimicrawler.xpath.core.Function;
import org.seimicrawler.xpath.core.Scope;
import org.seimicrawler.xpath.core.XValue;
import java.util.List;
/**
* Function: boolean starts-with(string, string)
* <p>
* The starts-with function returns true if the first argument string starts with the second argument string, and otherwise returns false.
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/26.
*/
public class StartsWith implements Function {
@Override
public String name() {
return "starts-with";
}
@Override
public XValue call(Scope scope, List<XValue> params) {
String first = params.get(0).asString();
String second = params.get(1).asString();
return XValue.create(first.startsWith(second));
}
}

@ -1,30 +0,0 @@
package org.seimicrawler.xpath.core.function;
import org.seimicrawler.xpath.core.Function;
import org.seimicrawler.xpath.core.Scope;
import org.seimicrawler.xpath.core.XValue;
import java.util.List;
/**
* number string-length(string?)
* The string-length returns the number of characters in the string (see [3.6 Strings]). If the argument is
* omitted, it defaults to the context node converted to a string, in other words the string-value of the context node.
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/27.
*/
public class StringLength implements Function {
@Override
public String name() {
return "string-length";
}
@Override
public XValue call(Scope scope, List<XValue> params) {
if (params == null || params.size() == 0) {
return XValue.create(0);
}
return XValue.create(params.get(0).asString().length());
}
}

@ -1,47 +0,0 @@
package org.seimicrawler.xpath.core.function;
import org.apache.commons.lang3.StringUtils;
import org.seimicrawler.xpath.core.Function;
import org.seimicrawler.xpath.core.Scope;
import org.seimicrawler.xpath.core.XValue;
import java.util.List;
/**
* Function: string substring(string, number, number?)
* https://www.w3.org/TR/1999/REC-xpath-19991116/#function-substring
* The substring function returns the substring of the first argument starting at the position specified in
* the second argument with length specified in the third argument. For example, substring("12345",2,3) returns "234".
* If the third argument is not specified, it returns the substring starting at the position specified in the
* second argument and continuing to the end of the string. For example, substring("12345",2) returns "2345".
* <p>
* substring("12345", 1.5, 2.6) returns "234"
* substring("12345", 0 `div` 0, 3) returns ""
* substring("12345", 1, 0 `div` 0) returns ""
* substring("12345", -42, 1 `div` 0) returns "12345"
* substring("12345", -1 `div` 0, 1 `div` 0) returns ""
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/26.
*/
public class SubString implements Function {
@Override
public String name() {
return "substring";
}
@Override
public XValue call(Scope scope, List<XValue> params) {
String target = params.get(0).asString();
int start = params.get(1).asLong().intValue();
start = Math.max(start - 1, 0);
if (params.get(2) != null) {
int end = params.get(2).asLong().intValue();
end = Math.min(start + end, target.length());
end = Math.max(end, 0);
return XValue.create(StringUtils.substring(target, start, end));
}
return XValue.create(StringUtils.substring(target, start));
}
}

@ -1,32 +0,0 @@
package org.seimicrawler.xpath.core.function;
import org.apache.commons.lang3.StringUtils;
import org.seimicrawler.xpath.core.Function;
import org.seimicrawler.xpath.core.Scope;
import org.seimicrawler.xpath.core.XValue;
import java.util.List;
/**
* Function: string substring-after(string, string)
* The substring-after function returns the substring of the first argument string that follows
* the first occurrence of the second argument string in the first argument string, or the empty string if
* the first argument string does not contain the second argument string.
* For example, substring-after("1999/04/01","/") returns 04/01, and substring-after("1999/04/01","19") returns 99/04/01.
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/26.
*/
public class SubStringAfter implements Function {
@Override
public String name() {
return "substring-after";
}
@Override
public XValue call(Scope scope, List<XValue> params) {
String target = params.get(0).asString();
String sep = params.get(1).asString();
return XValue.create(StringUtils.substringAfter(target, sep));
}
}

@ -1,32 +0,0 @@
package org.seimicrawler.xpath.core.function;
import org.apache.commons.lang3.StringUtils;
import org.seimicrawler.xpath.core.Function;
import org.seimicrawler.xpath.core.Scope;
import org.seimicrawler.xpath.core.XValue;
import java.util.List;
/**
* Function: string substring-after-last(string, string)
* The substring-after function returns the substring of the first argument string that follows
* the first occurrence of the second argument string in the first argument string, or the empty string if
* the first argument string does not contain the second argument string.
* For example, substring-after-last("1999/04/01","/") returns 01.
*
* @author github.com/zzldnl
* @since 2018/3/26.
*/
public class SubStringAfterLast implements Function {
@Override
public String name() {
return "substring-after-last";
}
@Override
public XValue call(Scope scope, List<XValue> params) {
String target = params.get(0).asString();
String sep = params.get(1).asString();
return XValue.create(StringUtils.substringAfterLast(target, sep));
}
}

@ -1,33 +0,0 @@
package org.seimicrawler.xpath.core.function;
import org.apache.commons.lang3.StringUtils;
import org.seimicrawler.xpath.core.Function;
import org.seimicrawler.xpath.core.Scope;
import org.seimicrawler.xpath.core.XValue;
import java.util.List;
/**
* Function: string substring-before(string, string)
* <p>
* The substring-before function returns the substring of the first argument string that precedes the
* first occurrence of the second argument string in the first argument string, or the empty string
* if the first argument string does not contain the second argument string.
* For example, substring-before("1999/04/01","/") returns 1999.
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/26.
*/
public class SubStringBefore implements Function {
@Override
public String name() {
return "substring-before";
}
@Override
public XValue call(Scope scope, List<XValue> params) {
String target = params.get(0).asString();
String sep = params.get(1).asString();
return XValue.create(StringUtils.substringBefore(target, sep));
}
}

@ -1,33 +0,0 @@
package org.seimicrawler.xpath.core.function;
import org.apache.commons.lang3.StringUtils;
import org.seimicrawler.xpath.core.Function;
import org.seimicrawler.xpath.core.Scope;
import org.seimicrawler.xpath.core.XValue;
import java.util.List;
/**
* Function: string substring-before-last(string, string)
* <p>
* The substring-before function returns the substring of the first argument string that precedes the
* first occurrence of the second argument string in the first argument string, or the empty string
* if the first argument string does not contain the second argument string.
* For example, substring-before-last("1999/04/01","/") returns 1999/04.
*
* @author github.com/zzldnl
* @since 2018/3/26.
*/
public class SubStringBeforeLast implements Function {
@Override
public String name() {
return "substring-before-last";
}
@Override
public XValue call(Scope scope, List<XValue> params) {
String target = params.get(0).asString();
String sep = params.get(1).asString();
return XValue.create(StringUtils.substringBeforeLast(target, sep));
}
}

@ -1,44 +0,0 @@
package org.seimicrawler.xpath.core.function;
import org.apache.commons.lang3.StringUtils;
import org.seimicrawler.xpath.core.Function;
import org.seimicrawler.xpath.core.Scope;
import org.seimicrawler.xpath.core.XValue;
import java.util.List;
/**
* JsoupXpath扩展函数(统一使用 jx )保持使用习惯java开发者习惯相同第一个数字为起始索引(且索引从0开始)第二个数字为结束索引
* <p>
* StringUtils.substring(null, *, *) = null
* StringUtils.substring("", * , *) = "";
* StringUtils.substring("abc", 0, 2) = "ab"
* StringUtils.substring("abc", 2, 0) = ""
* StringUtils.substring("abc", 2, 4) = "c"
* StringUtils.substring("abc", 2.13, 3.7) = "c"
* StringUtils.substring("abc", 4, 6) = ""
* StringUtils.substring("abc", 2, 2) = ""
* StringUtils.substring("abc", -2, -1) = "b"
* StringUtils.substring("abc", -4, 2) = "ab"
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/26.
*/
public class SubStringEx implements Function {
@Override
public String name() {
return "substring-ex";
}
@Override
public XValue call(Scope scope, List<XValue> params) {
String target = params.get(0).asString();
int start = params.get(1).asLong().intValue();
if (params.get(2) != null) {
int end = params.get(2).asLong().intValue();
return XValue.create(StringUtils.substring(target, start, end));
}
return XValue.create(StringUtils.substring(target, start));
}
}

@ -1,44 +0,0 @@
package org.seimicrawler.xpath.core.node;
import org.jsoup.nodes.Element;
import org.seimicrawler.xpath.core.NodeTest;
import org.seimicrawler.xpath.core.Scope;
import org.seimicrawler.xpath.core.XValue;
import java.util.LinkedList;
import java.util.List;
/**
* 获取当前节点下以及所有子孙节点中纯文本
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/26.
*/
public class AllText implements NodeTest {
/**
* 支持的函数名
*/
@Override
public String name() {
return "allText";
}
/**
* 函数具体逻辑
*
* @param scope 上下文
* @return 计算好的节点
*/
@Override
public XValue call(Scope scope) {
List<String> res = new LinkedList<>();
for (Element e : scope.context()) {
if ("script".equals(e.nodeName())) {
res.add(e.data());
} else {
res.add(e.text());
}
}
return XValue.create(res);
}
}

@ -1,31 +0,0 @@
package org.seimicrawler.xpath.core.node;
import org.jsoup.nodes.Element;
import org.seimicrawler.xpath.core.NodeTest;
import org.seimicrawler.xpath.core.Scope;
import org.seimicrawler.xpath.core.XValue;
import java.util.LinkedList;
import java.util.List;
/**
* 获取全部节点的内部的html
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/4/9.
*/
public class Html implements NodeTest {
@Override
public String name() {
return "html";
}
@Override
public XValue call(Scope scope) {
List<String> res = new LinkedList<>();
for (Element e : scope.context()) {
res.add(e.html());
}
return XValue.create(res);
}
}

@ -1,45 +0,0 @@
package org.seimicrawler.xpath.core.node;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.seimicrawler.xpath.core.NodeTest;
import org.seimicrawler.xpath.core.Scope;
import org.seimicrawler.xpath.core.XValue;
/**
* 获取当前节点下所有子节点以及独立文本
*
* @author: github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/4/4.
*/
public class Node implements NodeTest {
/**
* 支持的函数名
*/
@Override
public String name() {
return "node";
}
/**
* 函数具体逻辑
*
* @param scope 上下文
* @return 计算好的节点
*/
@Override
public XValue call(Scope scope) {
Elements context = new Elements();
for (Element el : scope.context()) {
context.addAll(el.children());
String txt = el.ownText();
if (StringUtils.isNotBlank(txt)) {
Element et = new Element("");
et.appendText(txt);
context.add(et);
}
}
return XValue.create(context);
}
}

@ -1,51 +0,0 @@
package org.seimicrawler.xpath.core.node;
import org.apache.commons.lang3.StringUtils;
import org.seimicrawler.xpath.core.NodeTest;
import org.seimicrawler.xpath.core.Scope;
import org.seimicrawler.xpath.core.XValue;
import org.seimicrawler.xpath.util.Scanner;
import java.math.BigDecimal;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 提取自由文本中的数字如果知道节点的自有文本(即非子代节点所包含的文本)中只存在一个数字如阅读数评论数价格等那么直接可以直接提取此数字出来
* 如果有多个数字将提取第一个匹配的连续数字支持小数返回double
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/26.
*/
public class Num implements NodeTest {
private static final Pattern numExt = Pattern.compile("\\d*\\.?\\d+");
/**
* 支持的函数名
*/
@Override
public String name() {
return "num";
}
/**
* 函数具体逻辑
*
* @param scope 上下文
* @return 计算好的节点
*/
@Override
public XValue call(Scope scope) {
NodeTest textFun = Scanner.findNodeTestByName("allText");
XValue textVal = textFun.call(scope);
String whole = StringUtils.join(textVal.asList(), "");
Matcher matcher = numExt.matcher(whole);
if (matcher.find()) {
String numStr = matcher.group();
BigDecimal num = new BigDecimal(numStr);
return XValue.create(num.doubleValue());
} else {
return XValue.create(null);
}
}
}

@ -1,31 +0,0 @@
package org.seimicrawler.xpath.core.node;
import org.jsoup.nodes.Element;
import org.seimicrawler.xpath.core.NodeTest;
import org.seimicrawler.xpath.core.Scope;
import org.seimicrawler.xpath.core.XValue;
import java.util.LinkedList;
import java.util.List;
/**
* 获取全部节点的 包含节点本身在内的全部html
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/4/9.
*/
public class OuterHtml implements NodeTest {
@Override
public String name() {
return "outerHtml";
}
@Override
public XValue call(Scope scope) {
List<String> res = new LinkedList<>();
for (Element e : scope.context()) {
res.add(e.outerHtml());
}
return XValue.create(res);
}
}

@ -1,108 +0,0 @@
package org.seimicrawler.xpath.core.node;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode;
import org.jsoup.select.Elements;
import org.jsoup.select.NodeTraversor;
import org.jsoup.select.NodeVisitor;
import org.seimicrawler.xpath.core.Constants;
import org.seimicrawler.xpath.core.NodeTest;
import org.seimicrawler.xpath.core.Scope;
import org.seimicrawler.xpath.core.XValue;
import org.seimicrawler.xpath.util.CommonUtil;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/2/28.
* `text()`不再简单的返回节点下的所有文本而是按照标准语义识别出多个文本块返回文本块列表
* ```
* <p> one <span> two</span> three </p>
* ```
* - `//text()` 返回 `["one", "two", "three" ]`
* - `//text()[2]` 返回 `["three"]`
*/
public class Text implements NodeTest {
/**
* 支持的函数名
*/
@Override
public String name() {
return "text";
}
/**
* 函数具体逻辑
*
* @param scope 上下文
* @return 计算好的节点
*/
@Override
public XValue call(Scope scope) {
Elements context = scope.context();
final Elements res = new Elements();
if (context != null && context.size() > 0) {
if (scope.isRecursion()) {
for (final Element e : context) {
final Map<String, Integer> indexMap = new HashMap<>();
NodeTraversor.traverse(new NodeVisitor() {
@Override
public void head(Node node, int depth) {
if (node instanceof TextNode) {
TextNode textNode = (TextNode) node;
String key = depth + "_" + textNode.parent().hashCode();
Integer index = indexMap.get(key);
if (index == null) {
index = 1;
} else {
index += 1;
}
indexMap.put(key, index);
Element data = new Element(Constants.DEF_TEXT_TAG_NAME);
data.text(textNode.getWholeText());
try {
Method parent = Node.class.getDeclaredMethod("setParentNode", Node.class);
parent.setAccessible(true);
parent.invoke(data, textNode.parent());
} catch (Exception e) {
//ignore
}
CommonUtil.setSameTagIndexInSiblings(data, index);
res.add(data);
}
}
@Override
public void tail(Node node, int depth) {
}
}, e);
}
} else {
for (Element e : context) {
if ("script".equals(e.nodeName())) {
Element data = new Element(Constants.DEF_TEXT_TAG_NAME);
data.text(e.data());
CommonUtil.setSameTagIndexInSiblings(data, 1);
res.add(data);
} else {
List<TextNode> textNodes = e.textNodes();
for (int i = 0; i < textNodes.size(); i++) {
TextNode textNode = textNodes.get(i);
Element data = new Element(Constants.DEF_TEXT_TAG_NAME);
data.text(textNode.getWholeText());
CommonUtil.setSameTagIndexInSiblings(data, i + 1);
res.add(data);
}
}
}
}
}
return XValue.create(res);
}
}

@ -1,35 +0,0 @@
package org.seimicrawler.xpath.exception;
import org.antlr.v4.runtime.DefaultErrorStrategy;
import org.antlr.v4.runtime.InputMismatchException;
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.misc.ParseCancellationException;
/**
* 如果语法解析异常直接抛出并终止解析
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/23.
*/
public class DoFailOnErrorHandler extends DefaultErrorStrategy {
@Override
public void recover(Parser recognizer, RecognitionException e) {
for (ParserRuleContext context = recognizer.getContext(); context != null; context = context.getParent()) {
context.exception = e;
}
throw new ParseCancellationException(e);
}
@Override
public Token recoverInline(Parser recognizer) throws RecognitionException {
InputMismatchException e = new InputMismatchException(recognizer);
for (ParserRuleContext context = recognizer.getContext(); context != null; context = context.getParent()) {
context.exception = e;
}
throw new ParseCancellationException(e);
}
}

@ -1,28 +0,0 @@
package org.seimicrawler.xpath.exception;
/*
Copyright 2014 Wang Haomiao<seimimaster@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* 使用不存在的轴语法则抛出此异常
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* Date: 14-3-15
*/
public class NoSuchAxisException extends RuntimeException {
public NoSuchAxisException(String msg) {
super(msg);
}
}

@ -1,26 +0,0 @@
package org.seimicrawler.xpath.exception;
/*
Copyright 2014 Wang Haomiao<seimimaster@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* Date: 14-3-16
*/
public class NoSuchFunctionException extends RuntimeException {
public NoSuchFunctionException(String msg) {
super(msg);
}
}

@ -1,13 +0,0 @@
package org.seimicrawler.xpath.exception;
/**
* 无法合并多个表达式的解析结果
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2017/12/5.
*/
public class XpathMergeValueException extends RuntimeException {
public XpathMergeValueException(String message) {
super(message);
}
}

@ -1,23 +0,0 @@
package org.seimicrawler.xpath.exception;
/**
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2017/12/5.
*/
public class XpathParserException extends RuntimeException {
/**
* Constructs a new runtime exception with the specified detail message.
* The cause is not initialized, and may subsequently be initialized by a
* call to {@link #initCause}.
*
* @param message the detail message. The detail message is saved for
* later retrieval by the {@link #getMessage()} method.
*/
public XpathParserException(String message) {
super(message);
}
public XpathParserException(String message, Throwable e) {
super(message, e);
}
}

@ -1,30 +0,0 @@
package org.seimicrawler.xpath.exception;
/*
Copyright 2014 Wang Haomiao<seimimaster@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 14-3-19
*/
public class XpathSyntaxErrorException extends RuntimeException {
public XpathSyntaxErrorException(String msg) {
super(msg);
}
public XpathSyntaxErrorException(String msg, Throwable e) {
super(msg, e);
}
}

@ -1,121 +0,0 @@
package org.seimicrawler.xpath.util;
/*
Copyright 2014 Wang Haomiao<seimimaster@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import org.apache.commons.lang3.StringUtils;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.seimicrawler.xpath.core.Constants;
import org.seimicrawler.xpath.core.Scope;
import java.util.Objects;
/**
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* Date: 14-3-15
*/
public class CommonUtil {
/**
* 获取同名元素在同胞中的index
*
* @param e
* @return
*/
public static int getElIndexInSameTags(Element e, Scope scope) {
Elements chs = e.parent().children();
int index = 1;
for (Element cur : chs) {
if (e.tagName().equals(cur.tagName()) && scope.context().contains(cur)) {
if (e.equals(cur)) {
break;
} else {
index += 1;
}
}
}
return index;
}
/**
* 获取同胞中同名元素的数量
*
* @param e
* @return
*/
public static int sameTagElNums(Element e, Scope scope) {
Elements context = new Elements();
Elements els = e.parent().getElementsByTag(e.tagName());
for (Element el : els) {
if (scope.context().contains(el)) {
context.add(el);
}
}
return context.size();
}
public static int getIndexInContext(Scope scope, Element el) {
for (int i = 0; i < scope.context().size(); i++) {
Element tmp = scope.context().get(i);
if (Objects.equals(tmp, el)) {
return i + 1;
}
}
return Integer.MIN_VALUE;
}
public static Elements followingSibling(Element el) {
Elements rs = new Elements();
Element tmp = el.nextElementSibling();
while (tmp != null) {
rs.add(tmp);
tmp = tmp.nextElementSibling();
}
if (rs.size() > 0) {
return rs;
}
return null;
}
public static Elements precedingSibling(Element el) {
Elements rs = new Elements();
Element tmp = el.previousElementSibling();
while (tmp != null) {
rs.add(tmp);
tmp = tmp.previousElementSibling();
}
if (rs.size() > 0) {
return rs;
}
return null;
}
public static void setSameTagIndexInSiblings(Element ori, int index) {
if (ori == null) {
return;
}
ori.attr(Constants.EL_SAME_TAG_INDEX_KEY, String.valueOf(index));
}
public static int getJxSameTagIndexInSiblings(Element ori) {
String val = ori.attr(Constants.EL_SAME_TAG_INDEX_KEY);
if (StringUtils.isBlank(val)) {
return -1;
}
return Integer.parseInt(val);
}
}

@ -1,141 +0,0 @@
package org.seimicrawler.xpath.util;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.seimicrawler.xpath.core.AxisSelector;
import org.seimicrawler.xpath.core.Function;
import org.seimicrawler.xpath.core.NodeTest;
import org.seimicrawler.xpath.core.axis.AncestorOrSelfSelector;
import org.seimicrawler.xpath.core.axis.AncestorSelector;
import org.seimicrawler.xpath.core.axis.AttributeSelector;
import org.seimicrawler.xpath.core.axis.ChildSelector;
import org.seimicrawler.xpath.core.axis.DescendantOrSelfSelector;
import org.seimicrawler.xpath.core.axis.DescendantSelector;
import org.seimicrawler.xpath.core.axis.FollowingSelector;
import org.seimicrawler.xpath.core.axis.FollowingSiblingOneSelector;
import org.seimicrawler.xpath.core.axis.FollowingSiblingSelector;
import org.seimicrawler.xpath.core.axis.ParentSelector;
import org.seimicrawler.xpath.core.axis.PrecedingSelector;
import org.seimicrawler.xpath.core.axis.PrecedingSiblingOneSelector;
import org.seimicrawler.xpath.core.axis.PrecedingSiblingSelector;
import org.seimicrawler.xpath.core.axis.SelfSelector;
import org.seimicrawler.xpath.core.function.Concat;
import org.seimicrawler.xpath.core.function.Contains;
import org.seimicrawler.xpath.core.function.Count;
import org.seimicrawler.xpath.core.function.First;
import org.seimicrawler.xpath.core.function.FormatDate;
import org.seimicrawler.xpath.core.function.Last;
import org.seimicrawler.xpath.core.function.Not;
import org.seimicrawler.xpath.core.function.Position;
import org.seimicrawler.xpath.core.function.StartsWith;
import org.seimicrawler.xpath.core.function.StringLength;
import org.seimicrawler.xpath.core.function.SubString;
import org.seimicrawler.xpath.core.function.SubStringAfter;
import org.seimicrawler.xpath.core.function.SubStringAfterLast;
import org.seimicrawler.xpath.core.function.SubStringBefore;
import org.seimicrawler.xpath.core.function.SubStringBeforeLast;
import org.seimicrawler.xpath.core.function.SubStringEx;
import org.seimicrawler.xpath.core.node.AllText;
import org.seimicrawler.xpath.core.node.Html;
import org.seimicrawler.xpath.core.node.Node;
import org.seimicrawler.xpath.core.node.Num;
import org.seimicrawler.xpath.core.node.OuterHtml;
import org.seimicrawler.xpath.core.node.Text;
import org.seimicrawler.xpath.exception.NoSuchAxisException;
import org.seimicrawler.xpath.exception.NoSuchFunctionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
/**
* 考虑更广泛的兼容性替换掉 FastClasspathScanner采用手工注册
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/2/28.
*/
public class Scanner {
private static final Map<String, AxisSelector> axisSelectorMap = new HashMap<>();
private static final Map<String, NodeTest> nodeTestMap = new HashMap<>();
private static final Map<String, Function> functionMap = new HashMap<>();
private static final Logger logger = LoggerFactory.getLogger(Scanner.class);
static {
initAxis(AncestorOrSelfSelector.class, AncestorSelector.class, AttributeSelector.class, ChildSelector.class, DescendantOrSelfSelector.class, DescendantSelector.class, FollowingSelector.class, FollowingSiblingOneSelector.class, FollowingSiblingSelector.class, ParentSelector.class, PrecedingSelector.class, PrecedingSiblingOneSelector.class, PrecedingSiblingSelector.class, SelfSelector.class);
initFunction(Concat.class, Contains.class, Count.class, First.class, Last.class, Not.class, Position.class, StartsWith.class, StringLength.class, SubString.class, SubStringAfter.class, SubStringBefore.class, SubStringEx.class, FormatDate.class, SubStringAfterLast.class, SubStringBeforeLast.class);
initNode(AllText.class, Html.class, Node.class, Num.class, OuterHtml.class, Text.class);
}
public static AxisSelector findSelectorByName(String selectorName) {
AxisSelector selector = axisSelectorMap.get(selectorName);
if (selector == null) {
throw new NoSuchAxisException("not support axis: " + selectorName);
}
return selector;
}
public static NodeTest findNodeTestByName(String nodeTestName) {
NodeTest nodeTest = nodeTestMap.get(nodeTestName);
if (nodeTest == null) {
throw new NoSuchFunctionException("not support nodeTest: " + nodeTestName);
}
return nodeTest;
}
public static Function findFunctionByName(String funcName) {
Function function = functionMap.get(funcName);
if (function == null) {
throw new NoSuchFunctionException("not support function: " + funcName);
}
return function;
}
public static void registerFunction(Class<? extends Function> func) {
Function function;
try {
function = func.newInstance();
functionMap.put(function.name(), function);
} catch (Exception e) {
logger.info(ExceptionUtils.getRootCauseMessage(e), e);
}
}
public static void registerNodeTest(Class<? extends NodeTest> nodeTestClass) {
NodeTest nodeTest;
try {
nodeTest = nodeTestClass.newInstance();
nodeTestMap.put(nodeTest.name(), nodeTest);
} catch (Exception e) {
logger.info(ExceptionUtils.getRootCauseMessage(e), e);
}
}
public static void registerAxisSelector(Class<? extends AxisSelector> axisSelectorClass) {
AxisSelector axisSelector;
try {
axisSelector = axisSelectorClass.newInstance();
axisSelectorMap.put(axisSelector.name(), axisSelector);
} catch (Exception e) {
logger.info(ExceptionUtils.getRootCauseMessage(e), e);
}
}
public static void initAxis(Class<? extends AxisSelector>... cls) {
for (Class<? extends AxisSelector> axis : cls) {
registerAxisSelector(axis);
}
}
public static void initFunction(Class<? extends Function>... cls) {
for (Class<? extends Function> func : cls) {
registerFunction(func);
}
}
public static void initNode(Class<? extends NodeTest>... cls) {
for (Class<? extends NodeTest> node : cls) {
registerNodeTest(node);
}
}
}

@ -1,296 +0,0 @@
grammar Xpath;
/*
XPath 1.0 grammar. Should conform to the official spec at
http://www.w3.org/TR/1999/REC-xpath-19991116. The grammar
rules have been kept as close as possible to those in the
spec, but some adjustmewnts were unavoidable. These were
mainly removing left recursion (spec seems to be based on
LR), and to deal with the double nature of the '*' token
(node wildcard and multiplication operator). See also
section 3.7 in the spec. These rule changes should make
no difference to the strings accepted by the grammar.
Written by Jan-Willem van den Broek
Version 1.0
Do with this code as you will.
*/
/*
Ported to Antlr4 by Tom Everett <tom@khubla.com>
*/
/**
操作符扩展:
a^=b 字符串a以字符串b开头 a startwith b
a*=b a包含b, a contains b
a$=b a以b结尾 a endwith b
a~=b a的内容符合 正则表达式b
a!~b a的内容不符合 正则表达式b
轴扩展:
following-sibling-one
preceding-sibling-one
sibling
NodeTest扩展:
num 抽取数字
allText 提取节点下全部文本
outerHtml 获取全部节点的 包含节点本身在内的全部html
html 获取全部节点的内部的html
*/
main : expr
;
locationPath
: relativeLocationPath
| absoluteLocationPathNoroot
;
absoluteLocationPathNoroot
: op=(PATHSEP|ABRPATH) relativeLocationPath
;
relativeLocationPath
: step (op=(PATHSEP|ABRPATH) step)*
;
step
: axisSpecifier nodeTest predicate*
| abbreviatedStep
;
axisSpecifier
: AxisName '::'
| '@'?
;
nodeTest: nameTest
| NodeType '(' ')'
| 'processing-instruction' '(' Literal ')'
;
predicate
: '[' expr ']'
;
abbreviatedStep
: '.'
| '..'
;
expr : orExpr
;
primaryExpr
: variableReference
| '(' expr ')'
| Literal
| Number
| functionCall
;
functionCall
: functionName '(' ( expr ( ',' expr )* )? ')'
;
unionExprNoRoot
: pathExprNoRoot (op=PIPE unionExprNoRoot)?
| PATHSEP PIPE unionExprNoRoot
;
pathExprNoRoot
: locationPath
| filterExpr (op=(PATHSEP|ABRPATH) relativeLocationPath)?
;
filterExpr
: primaryExpr predicate*
;
orExpr : andExpr ('or' andExpr)*
;
andExpr : equalityExpr ('and' equalityExpr)*
;
equalityExpr
: relationalExpr (op=(EQUALITY|INEQUALITY) relationalExpr)*
;
relationalExpr
: additiveExpr (op=(LESS|MORE_|LESS|GE|START_WITH|END_WITH|CONTAIN_WITH|REGEXP_WITH|REGEXP_NOT_WITH) additiveExpr)*
;
additiveExpr
: multiplicativeExpr (op=(PLUS|MINUS) multiplicativeExpr)*
;
multiplicativeExpr
: unaryExprNoRoot (op=(MUL|DIVISION|MODULO) multiplicativeExpr)?
// | '/' (op=('`div`'|'`mod`') multiplicativeExpr)?
;
unaryExprNoRoot
: (sign=MINUS)? unionExprNoRoot
;
qName : nCName (':' nCName)?
;
functionName
: qName // Does not match nodeType, as per spec.
;
variableReference
: '$' qName
;
nameTest: '*'
| nCName ':' '*'
| qName
;
nCName : NCName
| AxisName
;
NodeType: 'comment'
| 'text'
| 'processing-instruction'
| 'node'
| 'num' //抽取数字
| 'allText' //提取节点下全部文本
| 'outerHtml' //获取全部节点的 包含节点本身在内的全部html
| 'html' //获取全部节点的内部的html
;
Number : Digits ('.' Digits?)?
| '.' Digits
;
fragment
Digits : ('0'..'9')+
;
AxisName: 'ancestor'
| 'ancestor-or-self'
| 'attribute'
| 'child'
| 'descendant'
| 'descendant-or-self'
| 'following'
| 'following-sibling'
// | 'namespace'
| 'parent'
| 'preceding'
| 'preceding-sibling'
| 'self'
| 'following-sibling-one'
| 'preceding-sibling-one'
| 'sibling'
;
PATHSEP
:'/';
ABRPATH
: '//';
LPAR
: '(';
RPAR
: ')';
LBRAC
: '[';
RBRAC
: ']';
MINUS
: '-';
PLUS
: '+';
DOT
: '.';
MUL
: '*';
DIVISION
: '`div`';
MODULO
: '`mod`';
DOTDOT
: '..';
AT
: '@';
COMMA
: ',';
PIPE
: '|';
LESS
: '<';
MORE_
: '>';
LE
: '<=';
GE
: '>=';
EQUALITY
: '=';
INEQUALITY
: '!=';
START_WITH
: '^=';
END_WITH
: '$=';
CONTAIN_WITH
: '*=';
REGEXP_WITH
: '~=';
REGEXP_NOT_WITH
: '!~';
COLON
: ':';
CC
: '::';
APOS
: '\'';
QUOT
: '"';
Literal : '"' ~'"'* '"'
| '\'' ~'\''* '\''
;
Whitespace
: (' '|'\t'|'\n'|'\r')+ ->skip
;
NCName : NCNameStartChar NCNameChar*
;
fragment
NCNameStartChar
: 'A'..'Z'
| '_'
| 'a'..'z'
| '\u00C0'..'\u00D6'
| '\u00D8'..'\u00F6'
| '\u00F8'..'\u02FF'
| '\u0370'..'\u037D'
| '\u037F'..'\u1FFF'
| '\u200C'..'\u200D'
| '\u2070'..'\u218F'
| '\u2C00'..'\u2FEF'
| '\u3001'..'\uD7FF'
| '\uF900'..'\uFDCF'
| '\uFDF0'..'\uFFFD'
// Unfortunately, java escapes can't handle this conveniently,
// as they're limited to 4 hex digits. TODO.
// | '\U010000'..'\U0EFFFF'
;
fragment
NCNameChar
: NCNameStartChar | '-' | '.' | '0'..'9'
| '\u00B7' | '\u0300'..'\u036F'
| '\u203F'..'\u2040'
;

@ -1,12 +0,0 @@
package org.seimicrawler.xpath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author: github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2018/3/26.
*/
public class BaseTest {
protected Logger logger = LoggerFactory.getLogger(BaseTest.class);
}

@ -1,289 +0,0 @@
package org.seimicrawler.xpath;
import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.seimicrawler.xpath.exception.XpathSyntaxErrorException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* JXDocument Tester.
*
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @version 1.0
*/
@RunWith(DataProviderRunner.class)
public class JXDocumentTest {
private JXDocument underTest;
private JXDocument doubanTest;
private JXDocument custom;
private final ClassLoader loader = getClass().getClassLoader();
private final Logger logger = LoggerFactory.getLogger(JXDocumentTest.class);
@Before
public void before() throws Exception {
String html = "<html><body><script>console.log('aaaaa')</script><div class='test'>some body</div><div class='xiao'>Two</div></body></html>";
underTest = JXDocument.create(html);
if (doubanTest == null) {
URL t = loader.getResource("d_test.html");
assert t != null;
File dBook = new File(t.toURI());
String context = FileUtils.readFileToString(dBook, StandardCharsets.UTF_8);
doubanTest = JXDocument.create(context);
}
custom = JXDocument.create("<li><b>性别:</b>男</li>");
}
/**
* Method: sel(String xpath)
*/
@Test
public void testSel() throws Exception {
String xpath = "//script[1]/text()";
JXNode res = underTest.selNOne(xpath);
Assert.assertNotNull(res);
Assert.assertEquals("console.log('aaaaa')", res.asString());
}
@Test
public void testNotMatchFilter() throws Exception {
String xpath = "//div[contains(@class,'xiao')]/text()";
JXNode node = underTest.selNOne(xpath);
Assert.assertEquals("Two", node.asString());
}
@Test
@DataProvider(value = {
"//a/@href",
"//div[@class='paginator']/span[@class='next']/a/@href",
})
public void testXpath(String xpath) throws XpathSyntaxErrorException {
logger.info("current xpath: {}", xpath);
List<JXNode> rs = doubanTest.selN(xpath);
for (JXNode n : rs) {
if (!n.isString()) {
int index = n.asElement().siblingIndex();
logger.info("index = {}", index);
}
logger.info(n.toString());
}
}
/**
* d_test.html 来源于 https://book.douban.com/tag/%E4%BA%92%E8%81%94%E7%BD%91
* <p>
* 为了测试各种可能情况ul[@class='subject-list']节点以及其下内容被复制了一份出来并修改部分书名前缀为'T2-'以便区分
*/
@DataProvider
public static Object[][] dataOfXpathAndexpect() {
return new Object[][]{
{"//ul[@class='subject-list']/li[position()<3][last()]/div/h2/allText()", "黑客与画家 : 硅谷创业之父Paul Graham文集T2-黑客与画家 : 硅谷创业之父Paul Graham文集"},
{"//ul[@class='subject-list']/li[first()]/div/h2/allText()", "失控 : 全人类的最终命运和结局T2-失控 : 全人类的最终命运和结局"},
{"//ul[@class='subject-list']/li[./div/div/span[@class='pl']/num()>(1000+90*(2*50))][last()][1]/div/h2/allText()", "长尾理论长尾理论"},
{"//ul[@class='subject-list']/li[self::li/div/div/span[@class='pl']/num()>10000][-1]/div/h2/allText()", "长尾理论长尾理论"},
{"//ul[@class='subject-list']/li[contains(self::li/div/div/span[@class='pl']//text(),'14582')]/div/h2//text()", "黑客与画家: 硅谷创业之父Paul Graham文集T2-黑客与画家: 硅谷创业之父Paul Graham文集"},
{"//ul[@class='subject-list']/li[contains(./div/div/span[@class='pl']//text(),'14582')]/div/h2//text()", "黑客与画家: 硅谷创业之父Paul Graham文集T2-黑客与画家: 硅谷创业之父Paul Graham文集"},
{"//*[@id=\"subject_list\"]/ul/li[2]/div[2]/h2/a//text()", "黑客与画家: 硅谷创业之父Paul Graham文集T2-黑客与画家: 硅谷创业之父Paul Graham文集"},
{"//ul[@class]", 3L},
{"//a[@id]/@href", "https://www.douban.com/doumail/"},
{"//*[@id=\"subject_list\"]/ul[1]/li[8]/div[2]/div[2]/span[3]/num()", "3734.0"},
{"//a[@id]/@href | //*[@id=\"subject_list\"]/ul[1]/li[8]/div[2]/div[2]/span[3]/num()", "https://www.douban.com/doumail/3734.0"},
};
}
@UseDataProvider("dataOfXpathAndexpect")
@Test
public void testXpathAndAssert(String xpath, Object expect) throws XpathSyntaxErrorException {
logger.info("current xpath: {}", xpath);
List<JXNode> rs = doubanTest.selN(xpath);
if (expect instanceof String) {
String res = StringUtils.join(rs, "");
logger.info(res);
Assert.assertEquals(expect, res);
} else if (expect instanceof Number) {
long size = (long) expect;
Assert.assertEquals(size, rs.size());
}
}
@Test
@DataProvider(value = {
"//ul[@class='subject-list']/li[position()<3]"
})
public void testJXNode(String xpath) throws XpathSyntaxErrorException {
logger.info("current xpath: {}", xpath);
List<JXNode> jxNodeList = doubanTest.selN(xpath);
Set<String> expect = new HashSet<>();
//第一个 ul 中的
expect.add("失控: 全人类的最终命运和结局");
expect.add("黑客与画家: 硅谷创业之父Paul Graham文集");
//第二个 ul 中的
expect.add("T2-失控: 全人类的最终命运和结局");
expect.add("T2-黑客与画家: 硅谷创业之父Paul Graham文集");
Set<String> res = new HashSet<>();
for (JXNode node : jxNodeList) {
if (!node.isString()) {
String currentRes = StringUtils.join(node.sel("/div/h2/a//text()"), "");
logger.info(currentRes);
res.add(currentRes);
}
}
Assert.assertEquals(expect, res);
}
@Test
@DataProvider(value = {
"//ul[@class='subject-list']"
})
public void testRecursionNode(String xpath) throws XpathSyntaxErrorException {
logger.info("current xpath: {}", xpath);
List<JXNode> jxNodeList = doubanTest.selN(xpath);
logger.info("size = {}", jxNodeList.size());
// 有两个ul,下面的是为了测试特意复制添加的
Assert.assertEquals(2, jxNodeList.size());
}
@Test
@DataProvider(value = {
"//body/div/div/h1/text()",
"/body/div/div/h1/text()"
})
public void absolutePathTest(String xpath) throws XpathSyntaxErrorException {
logger.info("current xpath: {}", xpath);
List<JXNode> jxNodeList = doubanTest.selN(xpath);
logger.info("size = {},res ={}", jxNodeList.size(), jxNodeList);
}
@Test
public void testAs() throws XpathSyntaxErrorException {
List<JXNode> jxNodeList = custom.selN("//b[contains(text(),'性别')]/parent::*/text()");
Assert.assertEquals("男", StringUtils.join(jxNodeList, ""));
for (JXNode jxNode : jxNodeList) {
logger.info(jxNode.toString());
}
}
/**
* fix https://github.com/zhegexiaohuozi/JsoupXpath/issues/33
*/
// @Test
public void testNotObj() {
JXDocument doc = JXDocument.createByUrl("https://www.gxwztv.com/61/61514/");
// List<JXNode> nodes = doc.selN("//*[@id=\"chapters-list\"]/li[@style]");
List<JXNode> nodes = doc.selN("//*[@id=\"chapters-list\"]/li[not(@style)]");
for (JXNode node : nodes) {
logger.info("r = {}", node);
}
}
/**
* fix https://github.com/zhegexiaohuozi/JsoupXpath/issues/34
*/
@Test
public void testAttrAtRoot() {
String content = "<html>\n" +
" <head></head>\n" +
" <body>\n" +
" <a href=\"/124/124818/162585930.html\">第2章 神奇交流群</a>\n" +
" </body>\n" +
"</html>";
JXDocument doc = JXDocument.create(content);
List<JXNode> nodes = doc.selN("//@href");
for (JXNode node : nodes) {
logger.info("r = {}", node);
}
}
@Test
public void testA() {
String content = "<span style=\"color: #5191ce;\" >网页设计师</span>";
JXDocument doc = JXDocument.create(content);
List<JXNode> nodes = doc.selN("//*[text()='网页设计师']");
for (JXNode node : nodes) {
logger.info("r = {}", node);
}
}
/**
* fix https://github.com/zhegexiaohuozi/JsoupXpath/issues/52
*/
@Test
public void fixTextBehaviorTest() {
String html = "<p><span class=\"text-muted\">分类:</span>动漫<span class=\"split-line\"></span><span class=\"text-muted hidden-xs\">地区:</span>日本<span class=\"split-line\"></span><span class=\"text-muted hidden-xs\">年份:</span>2010</p>";
JXDocument jxDocument = JXDocument.create(html);
List<JXNode> jxNodes = jxDocument.selN("//text()[3]");
String actual = StringUtils.join(jxNodes, "");
logger.info("actual = {}", actual);
Assert.assertEquals("2010", actual);
List<JXNode> nodes = jxDocument.selN("//text()");
String allText = StringUtils.join(nodes, "");
Assert.assertEquals("分类:动漫地区:日本年份:2010", allText);
logger.info("all = {}", allText);
}
/**
* fix https://github.com/zhegexiaohuozi/JsoupXpath/issues/44
*/
@Test
public void fixTextElNoParentTest() {
String test = "<div class='a'> a <div>need</div> <div class='e'> not need</div> c </div>";
JXDocument j = JXDocument.create(test);
List<JXNode> l = j.selN("//div[@class='a']//text()[not(ancestor::div[@class='e'])]");
Set<String> finalRes = new HashSet<>();
for (JXNode i : l) {
logger.info("{}", i.toString());
finalRes.add(i.asString());
}
Assert.assertFalse(finalRes.contains("not need"));
Assert.assertTrue(finalRes.contains("need"));
Assert.assertEquals(4, finalRes.size());
}
/**
* fix https://github.com/zhegexiaohuozi/JsoupXpath/issues/53
*/
@Test
public void fixIssue53() {
String content = "<li class=\"res-book-item\" data-bid=\"1018351389\" data-rid=\"1\"> \n" +
" <div class=\"book-img-box\"> <a href=\"//book.qidian.com/info/1018351389\" target=\"_blank\" data-eid=\"qd_S04\" data-algrid=\"0.0.0\" data-bid=\"1018351389\"><img src=\"//bookcover.yuewen.com/qdbimg/349573/1018351389/150\"></a> \n" +
" </div> \n" +
" <div class=\"book-mid-info\"> \n" +
" <h4><a href=\"//book.qidian.com/info/1018351389\" target=\"_blank\" data-eid=\"qd_S05\" data-bid=\"1018351389\" data-algrid=\"0.0.0\"><cite class=\"red-kw\">我们</cite>平凡<cite class=\"red-kw\">我们</cite>忠诚</a></h4> \n" +
" <p class=\"author\"> <img src=\"//qidian.gtimg.com/qd/images/ico/user.f22d3.png\"><a class=\"name\" data-eid=\"qd_S06\" href=\"//my.qidian.com/author/403791004\" target=\"_blank\">巡璃</a> <em>|</em><a href=\"//www.qidian.com/duanpian\" data-eid=\"qd_S07\" target=\"_blank\">短篇</a><em>|</em><span>连载</span> </p> \n" +
" <p class=\"intro\"> 这是一位普通老兵的故事,这位老兵没有走上战场,也没有人歌颂他,但他的工作却是面对生与死,他是一名普通的军转干部,没有得到任何荣誉,却仍旧坚守着信仰,永远忠诚。除了他的家人,他的战友,他的故事不被任何人所知,但他的故事正是一代军人、一代军转干部的写照。所以,我来歌颂他,歌颂那一代人。 </p> \n" +
" <p class=\"update\"><a href=\"//read.qidian.com/chapter/YiObT_DmJpXu4xLcYRGW6w2/Ulsr6ThvJS5p4rPq4Fd4KQ2\" target=\"_blank\" data-eid=\"qd_S08\" data-bid=\"1018351389\" data-cid=\"//read.qidian.com/chapter/YiObT_DmJpXu4xLcYRGW6w2/Ulsr6ThvJS5p4rPq4Fd4KQ2\">最新更新 第一次见识到生死</a><em>·</em><span>2020-02-19</span> </p> \n" +
" </div> \n" +
" <div class=\"book-right-info\"> \n" +
" <div class=\"total\"> \n" +
" <p><span> 4497</span>总字数</p> \n" +
" <p><span> 0</span>总推荐</p> \n" +
" </div> \n" +
" <p class=\"btn\"> <a class=\"red-btn\" href=\"//book.qidian.com/info/1018351389\" data-eid=\"qd_S02\" target=\"_blank\">书籍详情</a> <a class=\"blue-btn add-book\" href=\"javascript:\" data-eid=\"qd_S03\" data-bookid=\"1018351389\" data-bid=\"1018351389\">加入书架</a> </p> \n" +
" </div> </li>";
JXDocument j = JXDocument.create(content);
List<JXNode> l = j.selN("//*[text()='总字数']//text()");
Assert.assertEquals(2, l.size());
Assert.assertEquals("4497", l.get(0).asString());
Assert.assertEquals("总字数", l.get(1).asString());
}
}

@ -1,49 +0,0 @@
package org.seimicrawler.xpath.core.function;
import org.junit.Test;
import org.seimicrawler.xpath.core.XValue;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
/**
* @description: TODO <br/>
* @create: 2019-01-21 21:07
* @author: zzldn@163.com
* @since JDK1.8
**/
public class DateFormatTest {
@Test
public void defaultTest() {
List<XValue> params = new LinkedList<>();
params.add(XValue.create("2019-01-21 19:05:42"));
params.add(XValue.create("yyyy-MM-dd HH:mm:ss"));
FormatDate formatDate = new FormatDate();
XValue value = formatDate.call(null, params);
System.out.println(value.asDate());
}
@Test
public void defaultTimeTest() {
List<XValue> params = new LinkedList<>();
params.add(XValue.create("19:05:42"));
params.add(XValue.create("HH:mm:ss"));
FormatDate formatDate = new FormatDate();
XValue value = formatDate.call(null, params);
System.out.println(value.asDate());
}
@Test
public void localTest() {
List<XValue> params = new LinkedList<>();
params.add(XValue.create("1/21/2019 07:05:42 AM"));
params.add(XValue.create("MM/dd/yyyy hh:mm:ss aa"));
params.add(XValue.create(Locale.ENGLISH.toString()));
FormatDate formatDate = new FormatDate();
XValue value = formatDate.call(null, params);
System.out.println(value.asDate());
}
}

@ -1,53 +0,0 @@
package org.seimicrawler.xpath.core.function;
import org.junit.Assert;
import org.junit.Test;
import org.seimicrawler.xpath.core.XValue;
import java.util.LinkedList;
import java.util.List;
/**
* SubString Tester.
*
* @author seimimaster@gmail.com
* @version 1.0
*/
public class SubStringTest {
/**
* substring("12345", 1.5, 2.6) returns "234"
* Method: call(Element context, List<XValue> params)
*/
@Test
public void testCall() throws Exception {
List<XValue> params = new LinkedList<>();
params.add(XValue.create("12345"));
params.add(XValue.create("1.5"));
params.add(XValue.create("2.6"));
SubString subStringFunc = new SubString();
Assert.assertEquals(subStringFunc.call(null, params).asString(), "234");
}
@Test
public void testZeroLength() throws Exception {
List<XValue> params = new LinkedList<>();
params.add(XValue.create("12345"));
params.add(XValue.create("2"));
params.add(XValue.create("-6"));
SubString subStringFunc = new SubString();
Assert.assertEquals(subStringFunc.call(null, params).asString(), "");
}
@Test
public void testOneLength() throws Exception {
List<XValue> params = new LinkedList<>();
params.add(XValue.create("12345"));
params.add(XValue.create("0"));
params.add(XValue.create("1"));
SubString subStringFunc = new SubString();
Assert.assertEquals(subStringFunc.call(null, params).asString(), "1");
}
}

@ -1,59 +0,0 @@
package org.seimicrawler.xpath.core.node;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.junit.Assert;
import org.junit.Test;
import org.seimicrawler.xpath.BaseTest;
import org.seimicrawler.xpath.core.Scope;
import org.seimicrawler.xpath.core.XValue;
/**
* Num Tester.
*
* @author seimimaster@gmail.com
* @version 1.0
*/
public class NumTest extends BaseTest {
/**
* Method: call(Elements context)
*/
@Test
public void testCall() throws Exception {
Elements context = new Elements();
Element el = new Element("V");
el.appendText("test 33.69");
context.add(el);
Num n = new Num();
XValue v = n.call(Scope.create(context));
logger.info("v = {}", v);
Assert.assertEquals(33.69, v.asDouble(), 0.00000000000001);
}
@Test
public void testShort() throws Exception {
Elements context = new Elements();
Element el = new Element("V");
el.appendText("test .69");
context.add(el);
Num n = new Num();
XValue v = n.call(Scope.create(context));
logger.info("v = {}", v);
Assert.assertEquals(0.69, v.asDouble(), 0.00000000000001);
}
@Test
public void testOnZero() throws Exception {
Elements context = new Elements();
Element el = new Element("V");
el.appendText("test 69.");
context.add(el);
Num n = new Num();
XValue v = n.call(Scope.create(context));
logger.info("v = {}", v);
Assert.assertEquals(69, v.asDouble(), 0.00000000000001);
}
}

@ -1,63 +0,0 @@
package org.seimicrawler.xpath.expr;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import org.apache.commons.io.FileUtils;
import org.jsoup.Jsoup;
import org.jsoup.select.Elements;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.seimicrawler.xpath.BaseTest;
import org.seimicrawler.xpath.antlr.XpathLexer;
import org.seimicrawler.xpath.antlr.XpathParser;
import org.seimicrawler.xpath.core.XValue;
import org.seimicrawler.xpath.core.XpathProcessor;
import org.seimicrawler.xpath.exception.DoFailOnErrorHandler;
import java.io.File;
import java.math.BigDecimal;
import java.net.URL;
import java.nio.charset.StandardCharsets;
/**
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2017/12/6.
*/
public class ExprTest extends BaseTest {
private Elements root;
private final ClassLoader loader = getClass().getClassLoader();
@Before
public void init() throws Exception {
// https://book.douban.com/tag/%E4%BA%92%E8%81%94%E7%BD%91
URL t = loader.getResource("d_test.html");
assert t != null;
File dBook = new File(t.toURI());
String context = FileUtils.readFileToString(dBook, StandardCharsets.UTF_8);
root = Jsoup.parse(context).children();
}
@Test
public void exp() {
String xpath = "//a[@id]/@href";
CharStream input = CharStreams.fromString(xpath);
XpathLexer lexer = new XpathLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
XpathParser parser = new XpathParser(tokens);
parser.setErrorHandler(new DoFailOnErrorHandler());
ParseTree tree = parser.main();
XpathProcessor processor = new XpathProcessor(root);
XValue value = processor.visit(tree);
logger.info("visit res = {}", value);
}
@Test
public void roundHalfUp() {
int x = new BigDecimal("5.53").setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
Assert.assertEquals(6, x);
}
}

File diff suppressed because it is too large Load Diff

@ -179,7 +179,7 @@ dependencies {
//
implementation('org.jsoup:jsoup:1.14.1')
implementation('com.jayway.jsonpath:json-path:2.6.0')
implementation(project(path: ':JsoupXpath'))
implementation('cn.wanghaomiao:JsoupXpath:2.5.0')
implementation(project(path: ':epublib'))
//JS rhino

@ -2,13 +2,13 @@
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp">
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
android:orientation="vertical"
android:padding="10dp">
<LinearLayout
android:id="@+id/ll_header_padding"
@ -19,17 +19,17 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:gravity="center_vertical"
android:orientation="horizontal">
android:orientation="horizontal"
android:paddingBottom="10dp">
<io.legado.app.ui.widget.text.AccentTextView
android:id="@+id/tv_header_padding"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="18sp"
android:text="@string/header" />
android:text="@string/header"
android:textSize="18sp" />
<TextView
android:layout_width="wrap_content"
@ -48,29 +48,29 @@
android:id="@+id/dsb_header_padding_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/padding_top"
app:max="100" />
app:max="100"
app:title="@string/padding_top" />
<io.legado.app.ui.widget.DetailSeekBar
android:id="@+id/dsb_header_padding_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/padding_bottom"
app:max="100" />
app:max="100"
app:title="@string/padding_bottom" />
<io.legado.app.ui.widget.DetailSeekBar
android:id="@+id/dsb_header_padding_left"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/padding_left"
app:max="100" />
app:max="100"
app:title="@string/padding_left" />
<io.legado.app.ui.widget.DetailSeekBar
android:id="@+id/dsb_header_padding_right"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/padding_right"
app:max="100" />
app:max="100"
app:title="@string/padding_right" />
</LinearLayout>
@ -80,36 +80,36 @@
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:textSize="18sp"
android:text="@string/main_body" />
android:text="@string/main_body"
android:textSize="18sp" />
<io.legado.app.ui.widget.DetailSeekBar
android:id="@+id/dsb_padding_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/padding_top"
app:max="200" />
app:max="200"
app:title="@string/padding_top" />
<io.legado.app.ui.widget.DetailSeekBar
android:id="@+id/dsb_padding_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/padding_bottom"
app:max="100" />
app:max="100"
app:title="@string/padding_bottom" />
<io.legado.app.ui.widget.DetailSeekBar
android:id="@+id/dsb_padding_left"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/padding_left"
app:max="100" />
app:max="100"
app:title="@string/padding_left" />
<io.legado.app.ui.widget.DetailSeekBar
android:id="@+id/dsb_padding_right"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/padding_right"
app:max="100" />
app:max="100"
app:title="@string/padding_right" />
<LinearLayout
android:layout_width="match_parent"
@ -123,8 +123,8 @@
android:layout_weight="1"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:textSize="18sp"
android:text="@string/footer" />
android:text="@string/footer"
android:textSize="18sp" />
<TextView
android:layout_width="wrap_content"
@ -143,29 +143,29 @@
android:id="@+id/dsb_footer_padding_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/padding_top"
app:max="100" />
app:max="100"
app:title="@string/padding_top" />
<io.legado.app.ui.widget.DetailSeekBar
android:id="@+id/dsb_footer_padding_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/padding_bottom"
app:max="100" />
app:max="100"
app:title="@string/padding_bottom" />
<io.legado.app.ui.widget.DetailSeekBar
android:id="@+id/dsb_footer_padding_left"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/padding_left"
app:max="100" />
app:max="100"
app:title="@string/padding_left" />
<io.legado.app.ui.widget.DetailSeekBar
android:id="@+id/dsb_footer_padding_right"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/padding_right"
app:max="100" />
app:max="100"
app:title="@string/padding_right" />
</LinearLayout>

@ -1,7 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
@ -318,4 +322,6 @@
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>

@ -1 +1 @@
include ':app',':JsoupXpath',':epublib'
include ':app',':epublib'

Loading…
Cancel
Save