getErrors() {
366 | return errors;
367 | }
368 |
369 | @Override
370 | public void syntaxError(Recognizer, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
371 | errors.add(new SyntaxError(line, charPositionInLine, (Token) offendingSymbol));
372 | }
373 |
374 | }
375 |
376 | public static void main(String...args) {
377 | EQLMapper mapper = new EQLMapper();
378 |
379 | String cql = "(and "
380 | + "(title any \"this is a test\")"
381 | + "(body adj \"phrase to match\")"
382 | + "(or "
383 | + "(title any \"term2\")"
384 | + "(title any/stemming \"term3\")"
385 | + "// this is a comment //"
386 | + ")"
387 | + ")";
388 |
389 |
390 | SyntaxTree result = EQLParser.parse(cql);
391 | System.out.println(mapper.toString(result.getRootNode(), "\n", "\t"));
392 |
393 | System.out.println(mapper.toHtml(result.getRootNode(), "div"));
394 |
395 | }
396 |
397 | }
398 |
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/antlr/Eql.tokens:
--------------------------------------------------------------------------------
1 | T__0=1
2 | T__1=2
3 | T__2=3
4 | T__3=4
5 | T__4=5
6 | T__5=6
7 | T__6=7
8 | T__7=8
9 | T__8=9
10 | T__9=10
11 | T__10=11
12 | T__11=12
13 | T__12=13
14 | AND=14
15 | OR=15
16 | NOT=16
17 | PROX=17
18 | CHARS=18
19 | WS=19
20 | '('=1
21 | ')'=2
22 | '//'=3
23 | '@ref'=4
24 | '=='=5
25 | '/'=6
26 | '='=7
27 | '>'=8
28 | '<'=9
29 | '>='=10
30 | '<='=11
31 | '<>'=12
32 | '"'=13
33 |
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/antlr/EqlBaseListener.java:
--------------------------------------------------------------------------------
1 | // Generated from /home/manosetro/IdeaProjects/cql-parser/parser/Eql.g4 by ANTLR 4.5.3
2 |
3 | package org.iptc.extra.core.eql.antlr;
4 |
5 |
6 | import org.antlr.v4.runtime.ParserRuleContext;
7 | import org.antlr.v4.runtime.tree.ErrorNode;
8 | import org.antlr.v4.runtime.tree.TerminalNode;
9 |
10 | /**
11 | * This class provides an empty implementation of {@link EqlListener},
12 | * which can be extended to create a listener which only needs to handle a subset
13 | * of the available methods.
14 | */
15 | public class EqlBaseListener implements EqlListener {
16 | /**
17 | * {@inheritDoc}
18 | *
19 | * The default implementation does nothing.
20 | */
21 | @Override public void enterPrefixClause(EqlParser.PrefixClauseContext ctx) { }
22 | /**
23 | * {@inheritDoc}
24 | *
25 | * The default implementation does nothing.
26 | */
27 | @Override public void exitPrefixClause(EqlParser.PrefixClauseContext ctx) { }
28 | /**
29 | * {@inheritDoc}
30 | *
31 | * The default implementation does nothing.
32 | */
33 | @Override public void enterStatement(EqlParser.StatementContext ctx) { }
34 | /**
35 | * {@inheritDoc}
36 | *
37 | * The default implementation does nothing.
38 | */
39 | @Override public void exitStatement(EqlParser.StatementContext ctx) { }
40 | /**
41 | * {@inheritDoc}
42 | *
43 | * The default implementation does nothing.
44 | */
45 | @Override public void enterBooleanOp(EqlParser.BooleanOpContext ctx) { }
46 | /**
47 | * {@inheritDoc}
48 | *
49 | * The default implementation does nothing.
50 | */
51 | @Override public void exitBooleanOp(EqlParser.BooleanOpContext ctx) { }
52 | /**
53 | * {@inheritDoc}
54 | *
55 | * The default implementation does nothing.
56 | */
57 | @Override public void enterSearchClause(EqlParser.SearchClauseContext ctx) { }
58 | /**
59 | * {@inheritDoc}
60 | *
61 | * The default implementation does nothing.
62 | */
63 | @Override public void exitSearchClause(EqlParser.SearchClauseContext ctx) { }
64 | /**
65 | * {@inheritDoc}
66 | *
67 | * The default implementation does nothing.
68 | */
69 | @Override public void enterCommentClause(EqlParser.CommentClauseContext ctx) { }
70 | /**
71 | * {@inheritDoc}
72 | *
73 | * The default implementation does nothing.
74 | */
75 | @Override public void exitCommentClause(EqlParser.CommentClauseContext ctx) { }
76 | /**
77 | * {@inheritDoc}
78 | *
79 | * The default implementation does nothing.
80 | */
81 | @Override public void enterReferenceClause(EqlParser.ReferenceClauseContext ctx) { }
82 | /**
83 | * {@inheritDoc}
84 | *
85 | * The default implementation does nothing.
86 | */
87 | @Override public void exitReferenceClause(EqlParser.ReferenceClauseContext ctx) { }
88 | /**
89 | * {@inheritDoc}
90 | *
91 | * The default implementation does nothing.
92 | */
93 | @Override public void enterReferencedRule(EqlParser.ReferencedRuleContext ctx) { }
94 | /**
95 | * {@inheritDoc}
96 | *
97 | * The default implementation does nothing.
98 | */
99 | @Override public void exitReferencedRule(EqlParser.ReferencedRuleContext ctx) { }
100 | /**
101 | * {@inheritDoc}
102 | *
103 | * The default implementation does nothing.
104 | */
105 | @Override public void enterRef(EqlParser.RefContext ctx) { }
106 | /**
107 | * {@inheritDoc}
108 | *
109 | * The default implementation does nothing.
110 | */
111 | @Override public void exitRef(EqlParser.RefContext ctx) { }
112 | /**
113 | * {@inheritDoc}
114 | *
115 | * The default implementation does nothing.
116 | */
117 | @Override public void enterRelation(EqlParser.RelationContext ctx) { }
118 | /**
119 | * {@inheritDoc}
120 | *
121 | * The default implementation does nothing.
122 | */
123 | @Override public void exitRelation(EqlParser.RelationContext ctx) { }
124 | /**
125 | * {@inheritDoc}
126 | *
127 | * The default implementation does nothing.
128 | */
129 | @Override public void enterModifierList(EqlParser.ModifierListContext ctx) { }
130 | /**
131 | * {@inheritDoc}
132 | *
133 | * The default implementation does nothing.
134 | */
135 | @Override public void exitModifierList(EqlParser.ModifierListContext ctx) { }
136 | /**
137 | * {@inheritDoc}
138 | *
139 | * The default implementation does nothing.
140 | */
141 | @Override public void enterModifier(EqlParser.ModifierContext ctx) { }
142 | /**
143 | * {@inheritDoc}
144 | *
145 | * The default implementation does nothing.
146 | */
147 | @Override public void exitModifier(EqlParser.ModifierContext ctx) { }
148 | /**
149 | * {@inheritDoc}
150 | *
151 | * The default implementation does nothing.
152 | */
153 | @Override public void enterComparitor(EqlParser.ComparitorContext ctx) { }
154 | /**
155 | * {@inheritDoc}
156 | *
157 | * The default implementation does nothing.
158 | */
159 | @Override public void exitComparitor(EqlParser.ComparitorContext ctx) { }
160 | /**
161 | * {@inheritDoc}
162 | *
163 | * The default implementation does nothing.
164 | */
165 | @Override public void enterNamedComparitor(EqlParser.NamedComparitorContext ctx) { }
166 | /**
167 | * {@inheritDoc}
168 | *
169 | * The default implementation does nothing.
170 | */
171 | @Override public void exitNamedComparitor(EqlParser.NamedComparitorContext ctx) { }
172 | /**
173 | * {@inheritDoc}
174 | *
175 | * The default implementation does nothing.
176 | */
177 | @Override public void enterComparitorSymbol(EqlParser.ComparitorSymbolContext ctx) { }
178 | /**
179 | * {@inheritDoc}
180 | *
181 | * The default implementation does nothing.
182 | */
183 | @Override public void exitComparitorSymbol(EqlParser.ComparitorSymbolContext ctx) { }
184 | /**
185 | * {@inheritDoc}
186 | *
187 | * The default implementation does nothing.
188 | */
189 | @Override public void enterModifierName(EqlParser.ModifierNameContext ctx) { }
190 | /**
191 | * {@inheritDoc}
192 | *
193 | * The default implementation does nothing.
194 | */
195 | @Override public void exitModifierName(EqlParser.ModifierNameContext ctx) { }
196 | /**
197 | * {@inheritDoc}
198 | *
199 | * The default implementation does nothing.
200 | */
201 | @Override public void enterModifierValue(EqlParser.ModifierValueContext ctx) { }
202 | /**
203 | * {@inheritDoc}
204 | *
205 | * The default implementation does nothing.
206 | */
207 | @Override public void exitModifierValue(EqlParser.ModifierValueContext ctx) { }
208 | /**
209 | * {@inheritDoc}
210 | *
211 | * The default implementation does nothing.
212 | */
213 | @Override public void enterSearchTerm(EqlParser.SearchTermContext ctx) { }
214 | /**
215 | * {@inheritDoc}
216 | *
217 | * The default implementation does nothing.
218 | */
219 | @Override public void exitSearchTerm(EqlParser.SearchTermContext ctx) { }
220 | /**
221 | * {@inheritDoc}
222 | *
223 | * The default implementation does nothing.
224 | */
225 | @Override public void enterIndex(EqlParser.IndexContext ctx) { }
226 | /**
227 | * {@inheritDoc}
228 | *
229 | * The default implementation does nothing.
230 | */
231 | @Override public void exitIndex(EqlParser.IndexContext ctx) { }
232 |
233 | /**
234 | * {@inheritDoc}
235 | *
236 | * The default implementation does nothing.
237 | */
238 | @Override public void enterEveryRule(ParserRuleContext ctx) { }
239 | /**
240 | * {@inheritDoc}
241 | *
242 | * The default implementation does nothing.
243 | */
244 | @Override public void exitEveryRule(ParserRuleContext ctx) { }
245 | /**
246 | * {@inheritDoc}
247 | *
248 | * The default implementation does nothing.
249 | */
250 | @Override public void visitTerminal(TerminalNode node) { }
251 | /**
252 | * {@inheritDoc}
253 | *
254 | * The default implementation does nothing.
255 | */
256 | @Override public void visitErrorNode(ErrorNode node) { }
257 | }
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/antlr/EqlBaseVisitor.java:
--------------------------------------------------------------------------------
1 | // Generated from /home/manosetro/IdeaProjects/cql-parser/parser/Eql.g4 by ANTLR 4.5.3
2 |
3 | package org.iptc.extra.core.eql.antlr;
4 |
5 | import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor;
6 |
7 | /**
8 | * This class provides an empty implementation of {@link EqlVisitor},
9 | * which can be extended to create a visitor which only needs to handle a subset
10 | * of the available methods.
11 | *
12 | * @param The return type of the visit operation. Use {@link Void} for
13 | * operations with no return type.
14 | */
15 | public class EqlBaseVisitor extends AbstractParseTreeVisitor implements EqlVisitor {
16 | /**
17 | * {@inheritDoc}
18 | *
19 | * The default implementation returns the result of calling
20 | * {@link #visitChildren} on {@code ctx}.
21 | */
22 | @Override public T visitPrefixClause(EqlParser.PrefixClauseContext ctx) { return visitChildren(ctx); }
23 | /**
24 | * {@inheritDoc}
25 | *
26 | * The default implementation returns the result of calling
27 | * {@link #visitChildren} on {@code ctx}.
28 | */
29 | @Override public T visitStatement(EqlParser.StatementContext ctx) { return visitChildren(ctx); }
30 | /**
31 | * {@inheritDoc}
32 | *
33 | * The default implementation returns the result of calling
34 | * {@link #visitChildren} on {@code ctx}.
35 | */
36 | @Override public T visitBooleanOp(EqlParser.BooleanOpContext ctx) { return visitChildren(ctx); }
37 | /**
38 | * {@inheritDoc}
39 | *
40 | * The default implementation returns the result of calling
41 | * {@link #visitChildren} on {@code ctx}.
42 | */
43 | @Override public T visitSearchClause(EqlParser.SearchClauseContext ctx) { return visitChildren(ctx); }
44 | /**
45 | * {@inheritDoc}
46 | *
47 | * The default implementation returns the result of calling
48 | * {@link #visitChildren} on {@code ctx}.
49 | */
50 | @Override public T visitCommentClause(EqlParser.CommentClauseContext ctx) { return visitChildren(ctx); }
51 | /**
52 | * {@inheritDoc}
53 | *
54 | * The default implementation returns the result of calling
55 | * {@link #visitChildren} on {@code ctx}.
56 | */
57 | @Override public T visitReferenceClause(EqlParser.ReferenceClauseContext ctx) { return visitChildren(ctx); }
58 | /**
59 | * {@inheritDoc}
60 | *
61 | * The default implementation returns the result of calling
62 | * {@link #visitChildren} on {@code ctx}.
63 | */
64 | @Override public T visitReferencedRule(EqlParser.ReferencedRuleContext ctx) { return visitChildren(ctx); }
65 | /**
66 | * {@inheritDoc}
67 | *
68 | * The default implementation returns the result of calling
69 | * {@link #visitChildren} on {@code ctx}.
70 | */
71 | @Override public T visitRef(EqlParser.RefContext ctx) { return visitChildren(ctx); }
72 | /**
73 | * {@inheritDoc}
74 | *
75 | * The default implementation returns the result of calling
76 | * {@link #visitChildren} on {@code ctx}.
77 | */
78 | @Override public T visitRelation(EqlParser.RelationContext ctx) { return visitChildren(ctx); }
79 | /**
80 | * {@inheritDoc}
81 | *
82 | * The default implementation returns the result of calling
83 | * {@link #visitChildren} on {@code ctx}.
84 | */
85 | @Override public T visitModifierList(EqlParser.ModifierListContext ctx) { return visitChildren(ctx); }
86 | /**
87 | * {@inheritDoc}
88 | *
89 | * The default implementation returns the result of calling
90 | * {@link #visitChildren} on {@code ctx}.
91 | */
92 | @Override public T visitModifier(EqlParser.ModifierContext ctx) { return visitChildren(ctx); }
93 | /**
94 | * {@inheritDoc}
95 | *
96 | * The default implementation returns the result of calling
97 | * {@link #visitChildren} on {@code ctx}.
98 | */
99 | @Override public T visitComparitor(EqlParser.ComparitorContext ctx) { return visitChildren(ctx); }
100 | /**
101 | * {@inheritDoc}
102 | *
103 | * The default implementation returns the result of calling
104 | * {@link #visitChildren} on {@code ctx}.
105 | */
106 | @Override public T visitNamedComparitor(EqlParser.NamedComparitorContext ctx) { return visitChildren(ctx); }
107 | /**
108 | * {@inheritDoc}
109 | *
110 | * The default implementation returns the result of calling
111 | * {@link #visitChildren} on {@code ctx}.
112 | */
113 | @Override public T visitComparitorSymbol(EqlParser.ComparitorSymbolContext ctx) { return visitChildren(ctx); }
114 | /**
115 | * {@inheritDoc}
116 | *
117 | * The default implementation returns the result of calling
118 | * {@link #visitChildren} on {@code ctx}.
119 | */
120 | @Override public T visitModifierName(EqlParser.ModifierNameContext ctx) { return visitChildren(ctx); }
121 | /**
122 | * {@inheritDoc}
123 | *
124 | * The default implementation returns the result of calling
125 | * {@link #visitChildren} on {@code ctx}.
126 | */
127 | @Override public T visitModifierValue(EqlParser.ModifierValueContext ctx) { return visitChildren(ctx); }
128 | /**
129 | * {@inheritDoc}
130 | *
131 | * The default implementation returns the result of calling
132 | * {@link #visitChildren} on {@code ctx}.
133 | */
134 | @Override public T visitSearchTerm(EqlParser.SearchTermContext ctx) { return visitChildren(ctx); }
135 | /**
136 | * {@inheritDoc}
137 | *
138 | * The default implementation returns the result of calling
139 | * {@link #visitChildren} on {@code ctx}.
140 | */
141 | @Override public T visitIndex(EqlParser.IndexContext ctx) { return visitChildren(ctx); }
142 | }
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/antlr/EqlLexer.java:
--------------------------------------------------------------------------------
1 | // Generated from /home/manosetro/IdeaProjects/cql-parser/parser/Eql.g4 by ANTLR 4.5.3
2 |
3 | package org.iptc.extra.core.eql.antlr;
4 |
5 | import org.antlr.v4.runtime.Lexer;
6 | import org.antlr.v4.runtime.CharStream;
7 | import org.antlr.v4.runtime.Token;
8 | import org.antlr.v4.runtime.TokenStream;
9 | import org.antlr.v4.runtime.*;
10 | import org.antlr.v4.runtime.atn.*;
11 | import org.antlr.v4.runtime.dfa.DFA;
12 | import org.antlr.v4.runtime.misc.*;
13 |
14 | @SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
15 | public class EqlLexer extends Lexer {
16 | static { RuntimeMetaData.checkVersion("4.5.3", RuntimeMetaData.VERSION); }
17 |
18 | protected static final DFA[] _decisionToDFA;
19 | protected static final PredictionContextCache _sharedContextCache =
20 | new PredictionContextCache();
21 | public static final int
22 | T__0=1, T__1=2, T__2=3, T__3=4, T__4=5, T__5=6, T__6=7, T__7=8, T__8=9,
23 | T__9=10, T__10=11, T__11=12, T__12=13, AND=14, OR=15, NOT=16, PROX=17,
24 | CHARS=18, WS=19;
25 | public static String[] modeNames = {
26 | "DEFAULT_MODE"
27 | };
28 |
29 | public static final String[] ruleNames = {
30 | "T__0", "T__1", "T__2", "T__3", "T__4", "T__5", "T__6", "T__7", "T__8",
31 | "T__9", "T__10", "T__11", "T__12", "AND", "OR", "NOT", "PROX", "CHARS",
32 | "WS"
33 | };
34 |
35 | private static final String[] _LITERAL_NAMES = {
36 | null, "'('", "')'", "'//'", "'@ref'", "'=='", "'/'", "'='", "'>'", "'<'",
37 | "'>='", "'<='", "'<>'", "'\"'"
38 | };
39 | private static final String[] _SYMBOLIC_NAMES = {
40 | null, null, null, null, null, null, null, null, null, null, null, null,
41 | null, null, "AND", "OR", "NOT", "PROX", "CHARS", "WS"
42 | };
43 | public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES);
44 |
45 | /**
46 | * @deprecated Use {@link #VOCABULARY} instead.
47 | */
48 | @Deprecated
49 | public static final String[] tokenNames;
50 | static {
51 | tokenNames = new String[_SYMBOLIC_NAMES.length];
52 | for (int i = 0; i < tokenNames.length; i++) {
53 | tokenNames[i] = VOCABULARY.getLiteralName(i);
54 | if (tokenNames[i] == null) {
55 | tokenNames[i] = VOCABULARY.getSymbolicName(i);
56 | }
57 |
58 | if (tokenNames[i] == null) {
59 | tokenNames[i] = "";
60 | }
61 | }
62 | }
63 |
64 | @Override
65 | @Deprecated
66 | public String[] getTokenNames() {
67 | return tokenNames;
68 | }
69 |
70 | @Override
71 |
72 | public Vocabulary getVocabulary() {
73 | return VOCABULARY;
74 | }
75 |
76 |
77 | public EqlLexer(CharStream input) {
78 | super(input);
79 | _interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache);
80 | }
81 |
82 | @Override
83 | public String getGrammarFileName() { return "Eql.g4"; }
84 |
85 | @Override
86 | public String[] getRuleNames() { return ruleNames; }
87 |
88 | @Override
89 | public String getSerializedATN() { return _serializedATN; }
90 |
91 | @Override
92 | public String[] getModeNames() { return modeNames; }
93 |
94 | @Override
95 | public ATN getATN() { return _ATN; }
96 |
97 | public static final String _serializedATN =
98 | "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\2\25w\b\1\4\2\t\2\4"+
99 | "\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"+
100 | "\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"+
101 | "\4\23\t\23\4\24\t\24\3\2\3\2\3\3\3\3\3\4\3\4\3\4\3\5\3\5\3\5\3\5\3\5\3"+
102 | "\6\3\6\3\6\3\7\3\7\3\b\3\b\3\t\3\t\3\n\3\n\3\13\3\13\3\13\3\f\3\f\3\f"+
103 | "\3\r\3\r\3\r\3\16\3\16\3\17\3\17\3\17\3\17\3\17\3\17\5\17R\n\17\3\20\3"+
104 | "\20\3\20\3\20\5\20X\n\20\3\21\3\21\3\21\3\21\3\21\3\21\5\21`\n\21\3\22"+
105 | "\3\22\3\22\3\22\3\22\3\22\3\22\3\22\5\22j\n\22\3\23\6\23m\n\23\r\23\16"+
106 | "\23n\3\24\6\24r\n\24\r\24\16\24s\3\24\3\24\2\2\25\3\3\5\4\7\5\t\6\13\7"+
107 | "\r\b\17\t\21\n\23\13\25\f\27\r\31\16\33\17\35\20\37\21!\22#\23%\24\'\25"+
108 | "\3\2\4\b\2\f\f\"\"$$*+\61\61>@\5\2\13\f\17\17\"\"|\2\3\3\2\2\2\2\5\3\2"+
109 | "\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\17\3\2\2\2\2\21"+
110 | "\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"+
111 | "\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"+
112 | "\2\2\2\3)\3\2\2\2\5+\3\2\2\2\7-\3\2\2\2\t\60\3\2\2\2\13\65\3\2\2\2\r8"+
113 | "\3\2\2\2\17:\3\2\2\2\21<\3\2\2\2\23>\3\2\2\2\25@\3\2\2\2\27C\3\2\2\2\31"+
114 | "F\3\2\2\2\33I\3\2\2\2\35Q\3\2\2\2\37W\3\2\2\2!_\3\2\2\2#i\3\2\2\2%l\3"+
115 | "\2\2\2\'q\3\2\2\2)*\7*\2\2*\4\3\2\2\2+,\7+\2\2,\6\3\2\2\2-.\7\61\2\2."+
116 | "/\7\61\2\2/\b\3\2\2\2\60\61\7B\2\2\61\62\7t\2\2\62\63\7g\2\2\63\64\7h"+
117 | "\2\2\64\n\3\2\2\2\65\66\7?\2\2\66\67\7?\2\2\67\f\3\2\2\289\7\61\2\29\16"+
118 | "\3\2\2\2:;\7?\2\2;\20\3\2\2\2<=\7@\2\2=\22\3\2\2\2>?\7>\2\2?\24\3\2\2"+
119 | "\2@A\7@\2\2AB\7?\2\2B\26\3\2\2\2CD\7>\2\2DE\7?\2\2E\30\3\2\2\2FG\7>\2"+
120 | "\2GH\7@\2\2H\32\3\2\2\2IJ\7$\2\2J\34\3\2\2\2KL\7C\2\2LM\7P\2\2MR\7F\2"+
121 | "\2NO\7c\2\2OP\7p\2\2PR\7f\2\2QK\3\2\2\2QN\3\2\2\2R\36\3\2\2\2ST\7Q\2\2"+
122 | "TX\7T\2\2UV\7q\2\2VX\7t\2\2WS\3\2\2\2WU\3\2\2\2X \3\2\2\2YZ\7P\2\2Z[\7"+
123 | "Q\2\2[`\7V\2\2\\]\7p\2\2]^\7q\2\2^`\7v\2\2_Y\3\2\2\2_\\\3\2\2\2`\"\3\2"+
124 | "\2\2ab\7R\2\2bc\7T\2\2cd\7Q\2\2dj\7Z\2\2ef\7r\2\2fg\7t\2\2gh\7q\2\2hj"+
125 | "\7z\2\2ia\3\2\2\2ie\3\2\2\2j$\3\2\2\2km\n\2\2\2lk\3\2\2\2mn\3\2\2\2nl"+
126 | "\3\2\2\2no\3\2\2\2o&\3\2\2\2pr\t\3\2\2qp\3\2\2\2rs\3\2\2\2sq\3\2\2\2s"+
127 | "t\3\2\2\2tu\3\2\2\2uv\b\24\2\2v(\3\2\2\2\t\2QW_ins\3\2\3\2";
128 | public static final ATN _ATN =
129 | new ATNDeserializer().deserialize(_serializedATN.toCharArray());
130 | static {
131 | _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()];
132 | for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) {
133 | _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i);
134 | }
135 | }
136 | }
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/antlr/EqlLexer.tokens:
--------------------------------------------------------------------------------
1 | T__0=1
2 | T__1=2
3 | T__2=3
4 | T__3=4
5 | T__4=5
6 | T__5=6
7 | T__6=7
8 | T__7=8
9 | T__8=9
10 | T__9=10
11 | T__10=11
12 | T__11=12
13 | T__12=13
14 | AND=14
15 | OR=15
16 | NOT=16
17 | PROX=17
18 | CHARS=18
19 | WS=19
20 | '('=1
21 | ')'=2
22 | '//'=3
23 | '@ref'=4
24 | '=='=5
25 | '/'=6
26 | '='=7
27 | '>'=8
28 | '<'=9
29 | '>='=10
30 | '<='=11
31 | '<>'=12
32 | '"'=13
33 |
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/antlr/EqlListener.java:
--------------------------------------------------------------------------------
1 | // Generated from /home/manosetro/IdeaProjects/cql-parser/parser/Eql.g4 by ANTLR 4.5.3
2 |
3 | package org.iptc.extra.core.eql.antlr;
4 |
5 | import org.antlr.v4.runtime.tree.ParseTreeListener;
6 |
7 | /**
8 | * This interface defines a complete listener for a parse tree produced by
9 | * {@link EqlParser}.
10 | */
11 | public interface EqlListener extends ParseTreeListener {
12 | /**
13 | * Enter a parse tree produced by {@link EqlParser#prefixClause}.
14 | * @param ctx the parse tree
15 | */
16 | void enterPrefixClause(EqlParser.PrefixClauseContext ctx);
17 | /**
18 | * Exit a parse tree produced by {@link EqlParser#prefixClause}.
19 | * @param ctx the parse tree
20 | */
21 | void exitPrefixClause(EqlParser.PrefixClauseContext ctx);
22 | /**
23 | * Enter a parse tree produced by {@link EqlParser#statement}.
24 | * @param ctx the parse tree
25 | */
26 | void enterStatement(EqlParser.StatementContext ctx);
27 | /**
28 | * Exit a parse tree produced by {@link EqlParser#statement}.
29 | * @param ctx the parse tree
30 | */
31 | void exitStatement(EqlParser.StatementContext ctx);
32 | /**
33 | * Enter a parse tree produced by {@link EqlParser#booleanOp}.
34 | * @param ctx the parse tree
35 | */
36 | void enterBooleanOp(EqlParser.BooleanOpContext ctx);
37 | /**
38 | * Exit a parse tree produced by {@link EqlParser#booleanOp}.
39 | * @param ctx the parse tree
40 | */
41 | void exitBooleanOp(EqlParser.BooleanOpContext ctx);
42 | /**
43 | * Enter a parse tree produced by {@link EqlParser#searchClause}.
44 | * @param ctx the parse tree
45 | */
46 | void enterSearchClause(EqlParser.SearchClauseContext ctx);
47 | /**
48 | * Exit a parse tree produced by {@link EqlParser#searchClause}.
49 | * @param ctx the parse tree
50 | */
51 | void exitSearchClause(EqlParser.SearchClauseContext ctx);
52 | /**
53 | * Enter a parse tree produced by {@link EqlParser#commentClause}.
54 | * @param ctx the parse tree
55 | */
56 | void enterCommentClause(EqlParser.CommentClauseContext ctx);
57 | /**
58 | * Exit a parse tree produced by {@link EqlParser#commentClause}.
59 | * @param ctx the parse tree
60 | */
61 | void exitCommentClause(EqlParser.CommentClauseContext ctx);
62 | /**
63 | * Enter a parse tree produced by {@link EqlParser#referenceClause}.
64 | * @param ctx the parse tree
65 | */
66 | void enterReferenceClause(EqlParser.ReferenceClauseContext ctx);
67 | /**
68 | * Exit a parse tree produced by {@link EqlParser#referenceClause}.
69 | * @param ctx the parse tree
70 | */
71 | void exitReferenceClause(EqlParser.ReferenceClauseContext ctx);
72 | /**
73 | * Enter a parse tree produced by {@link EqlParser#referencedRule}.
74 | * @param ctx the parse tree
75 | */
76 | void enterReferencedRule(EqlParser.ReferencedRuleContext ctx);
77 | /**
78 | * Exit a parse tree produced by {@link EqlParser#referencedRule}.
79 | * @param ctx the parse tree
80 | */
81 | void exitReferencedRule(EqlParser.ReferencedRuleContext ctx);
82 | /**
83 | * Enter a parse tree produced by {@link EqlParser#ref}.
84 | * @param ctx the parse tree
85 | */
86 | void enterRef(EqlParser.RefContext ctx);
87 | /**
88 | * Exit a parse tree produced by {@link EqlParser#ref}.
89 | * @param ctx the parse tree
90 | */
91 | void exitRef(EqlParser.RefContext ctx);
92 | /**
93 | * Enter a parse tree produced by {@link EqlParser#relation}.
94 | * @param ctx the parse tree
95 | */
96 | void enterRelation(EqlParser.RelationContext ctx);
97 | /**
98 | * Exit a parse tree produced by {@link EqlParser#relation}.
99 | * @param ctx the parse tree
100 | */
101 | void exitRelation(EqlParser.RelationContext ctx);
102 | /**
103 | * Enter a parse tree produced by {@link EqlParser#modifierList}.
104 | * @param ctx the parse tree
105 | */
106 | void enterModifierList(EqlParser.ModifierListContext ctx);
107 | /**
108 | * Exit a parse tree produced by {@link EqlParser#modifierList}.
109 | * @param ctx the parse tree
110 | */
111 | void exitModifierList(EqlParser.ModifierListContext ctx);
112 | /**
113 | * Enter a parse tree produced by {@link EqlParser#modifier}.
114 | * @param ctx the parse tree
115 | */
116 | void enterModifier(EqlParser.ModifierContext ctx);
117 | /**
118 | * Exit a parse tree produced by {@link EqlParser#modifier}.
119 | * @param ctx the parse tree
120 | */
121 | void exitModifier(EqlParser.ModifierContext ctx);
122 | /**
123 | * Enter a parse tree produced by {@link EqlParser#comparitor}.
124 | * @param ctx the parse tree
125 | */
126 | void enterComparitor(EqlParser.ComparitorContext ctx);
127 | /**
128 | * Exit a parse tree produced by {@link EqlParser#comparitor}.
129 | * @param ctx the parse tree
130 | */
131 | void exitComparitor(EqlParser.ComparitorContext ctx);
132 | /**
133 | * Enter a parse tree produced by {@link EqlParser#namedComparitor}.
134 | * @param ctx the parse tree
135 | */
136 | void enterNamedComparitor(EqlParser.NamedComparitorContext ctx);
137 | /**
138 | * Exit a parse tree produced by {@link EqlParser#namedComparitor}.
139 | * @param ctx the parse tree
140 | */
141 | void exitNamedComparitor(EqlParser.NamedComparitorContext ctx);
142 | /**
143 | * Enter a parse tree produced by {@link EqlParser#comparitorSymbol}.
144 | * @param ctx the parse tree
145 | */
146 | void enterComparitorSymbol(EqlParser.ComparitorSymbolContext ctx);
147 | /**
148 | * Exit a parse tree produced by {@link EqlParser#comparitorSymbol}.
149 | * @param ctx the parse tree
150 | */
151 | void exitComparitorSymbol(EqlParser.ComparitorSymbolContext ctx);
152 | /**
153 | * Enter a parse tree produced by {@link EqlParser#modifierName}.
154 | * @param ctx the parse tree
155 | */
156 | void enterModifierName(EqlParser.ModifierNameContext ctx);
157 | /**
158 | * Exit a parse tree produced by {@link EqlParser#modifierName}.
159 | * @param ctx the parse tree
160 | */
161 | void exitModifierName(EqlParser.ModifierNameContext ctx);
162 | /**
163 | * Enter a parse tree produced by {@link EqlParser#modifierValue}.
164 | * @param ctx the parse tree
165 | */
166 | void enterModifierValue(EqlParser.ModifierValueContext ctx);
167 | /**
168 | * Exit a parse tree produced by {@link EqlParser#modifierValue}.
169 | * @param ctx the parse tree
170 | */
171 | void exitModifierValue(EqlParser.ModifierValueContext ctx);
172 | /**
173 | * Enter a parse tree produced by {@link EqlParser#searchTerm}.
174 | * @param ctx the parse tree
175 | */
176 | void enterSearchTerm(EqlParser.SearchTermContext ctx);
177 | /**
178 | * Exit a parse tree produced by {@link EqlParser#searchTerm}.
179 | * @param ctx the parse tree
180 | */
181 | void exitSearchTerm(EqlParser.SearchTermContext ctx);
182 | /**
183 | * Enter a parse tree produced by {@link EqlParser#index}.
184 | * @param ctx the parse tree
185 | */
186 | void enterIndex(EqlParser.IndexContext ctx);
187 | /**
188 | * Exit a parse tree produced by {@link EqlParser#index}.
189 | * @param ctx the parse tree
190 | */
191 | void exitIndex(EqlParser.IndexContext ctx);
192 | }
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/antlr/EqlVisitor.java:
--------------------------------------------------------------------------------
1 | // Generated from /home/manosetro/IdeaProjects/cql-parser/parser/Eql.g4 by ANTLR 4.5.3
2 |
3 | package org.iptc.extra.core.eql.antlr;
4 |
5 | import org.antlr.v4.runtime.tree.ParseTreeVisitor;
6 |
7 | /**
8 | * This interface defines a complete generic visitor for a parse tree produced
9 | * by {@link EqlParser}.
10 | *
11 | * @param The return type of the visit operation. Use {@link Void} for
12 | * operations with no return type.
13 | */
14 | public interface EqlVisitor extends ParseTreeVisitor {
15 | /**
16 | * Visit a parse tree produced by {@link EqlParser#prefixClause}.
17 | * @param ctx the parse tree
18 | * @return the visitor result
19 | */
20 | T visitPrefixClause(EqlParser.PrefixClauseContext ctx);
21 | /**
22 | * Visit a parse tree produced by {@link EqlParser#statement}.
23 | * @param ctx the parse tree
24 | * @return the visitor result
25 | */
26 | T visitStatement(EqlParser.StatementContext ctx);
27 | /**
28 | * Visit a parse tree produced by {@link EqlParser#booleanOp}.
29 | * @param ctx the parse tree
30 | * @return the visitor result
31 | */
32 | T visitBooleanOp(EqlParser.BooleanOpContext ctx);
33 | /**
34 | * Visit a parse tree produced by {@link EqlParser#searchClause}.
35 | * @param ctx the parse tree
36 | * @return the visitor result
37 | */
38 | T visitSearchClause(EqlParser.SearchClauseContext ctx);
39 | /**
40 | * Visit a parse tree produced by {@link EqlParser#commentClause}.
41 | * @param ctx the parse tree
42 | * @return the visitor result
43 | */
44 | T visitCommentClause(EqlParser.CommentClauseContext ctx);
45 | /**
46 | * Visit a parse tree produced by {@link EqlParser#referenceClause}.
47 | * @param ctx the parse tree
48 | * @return the visitor result
49 | */
50 | T visitReferenceClause(EqlParser.ReferenceClauseContext ctx);
51 | /**
52 | * Visit a parse tree produced by {@link EqlParser#referencedRule}.
53 | * @param ctx the parse tree
54 | * @return the visitor result
55 | */
56 | T visitReferencedRule(EqlParser.ReferencedRuleContext ctx);
57 | /**
58 | * Visit a parse tree produced by {@link EqlParser#ref}.
59 | * @param ctx the parse tree
60 | * @return the visitor result
61 | */
62 | T visitRef(EqlParser.RefContext ctx);
63 | /**
64 | * Visit a parse tree produced by {@link EqlParser#relation}.
65 | * @param ctx the parse tree
66 | * @return the visitor result
67 | */
68 | T visitRelation(EqlParser.RelationContext ctx);
69 | /**
70 | * Visit a parse tree produced by {@link EqlParser#modifierList}.
71 | * @param ctx the parse tree
72 | * @return the visitor result
73 | */
74 | T visitModifierList(EqlParser.ModifierListContext ctx);
75 | /**
76 | * Visit a parse tree produced by {@link EqlParser#modifier}.
77 | * @param ctx the parse tree
78 | * @return the visitor result
79 | */
80 | T visitModifier(EqlParser.ModifierContext ctx);
81 | /**
82 | * Visit a parse tree produced by {@link EqlParser#comparitor}.
83 | * @param ctx the parse tree
84 | * @return the visitor result
85 | */
86 | T visitComparitor(EqlParser.ComparitorContext ctx);
87 | /**
88 | * Visit a parse tree produced by {@link EqlParser#namedComparitor}.
89 | * @param ctx the parse tree
90 | * @return the visitor result
91 | */
92 | T visitNamedComparitor(EqlParser.NamedComparitorContext ctx);
93 | /**
94 | * Visit a parse tree produced by {@link EqlParser#comparitorSymbol}.
95 | * @param ctx the parse tree
96 | * @return the visitor result
97 | */
98 | T visitComparitorSymbol(EqlParser.ComparitorSymbolContext ctx);
99 | /**
100 | * Visit a parse tree produced by {@link EqlParser#modifierName}.
101 | * @param ctx the parse tree
102 | * @return the visitor result
103 | */
104 | T visitModifierName(EqlParser.ModifierNameContext ctx);
105 | /**
106 | * Visit a parse tree produced by {@link EqlParser#modifierValue}.
107 | * @param ctx the parse tree
108 | * @return the visitor result
109 | */
110 | T visitModifierValue(EqlParser.ModifierValueContext ctx);
111 | /**
112 | * Visit a parse tree produced by {@link EqlParser#searchTerm}.
113 | * @param ctx the parse tree
114 | * @return the visitor result
115 | */
116 | T visitSearchTerm(EqlParser.SearchTermContext ctx);
117 | /**
118 | * Visit a parse tree produced by {@link EqlParser#index}.
119 | * @param ctx the parse tree
120 | * @return the visitor result
121 | */
122 | T visitIndex(EqlParser.IndexContext ctx);
123 | }
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/tree/SyntaxError.java:
--------------------------------------------------------------------------------
1 | package org.iptc.extra.core.eql.tree;
2 |
3 | import org.antlr.v4.runtime.Token;
4 |
5 | /**
6 | * @author manosetro
7 | *
8 | */
9 | public class SyntaxError {
10 |
11 | private int line;
12 | private int position;
13 | private String offendingToken;
14 | private int[] offendingTokenPosition = new int[2];
15 |
16 | public SyntaxError(int line, int position, Token offendingToken) {
17 | this.line = line;
18 | this.position = position;
19 | this.offendingToken = offendingToken.getText();
20 | this.offendingTokenPosition[0] = offendingToken.getStartIndex();
21 | this.offendingTokenPosition[1] = offendingToken.getStopIndex();
22 | }
23 |
24 | public int getLine() {
25 | return line;
26 | }
27 |
28 | public void setLine(int line) {
29 | this.line = line;
30 | }
31 |
32 | public int getPosition() {
33 | return position;
34 | }
35 |
36 | public void setPosition(int position) {
37 | this.position = position;
38 | }
39 |
40 | public String getOffendingToken() {
41 | return offendingToken;
42 | }
43 |
44 | public void setOffendingToken(String offendingToken) {
45 | this.offendingToken = offendingToken;
46 | }
47 |
48 | public int[] getOffendingTokenPosition() {
49 | return offendingTokenPosition;
50 | }
51 |
52 | public void setOffendingTokenPosition(int[] offendingTokenPosition) {
53 | this.offendingTokenPosition = offendingTokenPosition;
54 | }
55 |
56 | public String toString() {
57 | return "Syntax Error at line " + line + ":" + position + ". Offending Token: " + offendingToken + " [" + offendingTokenPosition[0] + ":" + offendingTokenPosition[1] + "]";
58 | }
59 | }
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/tree/SyntaxTree.java:
--------------------------------------------------------------------------------
1 | package org.iptc.extra.core.eql.tree;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import org.iptc.extra.core.eql.tree.nodes.Node;
7 |
8 | /**
9 | * @author manosetro
10 | *
11 | * SyntaxTree class is the result of parsing of a given EQL rule.
12 | *
13 | */
14 | public class SyntaxTree {
15 |
16 | private Node root; // the root node of the syntax tree. Is the entry node for each class used to traverse the tree.
17 |
18 | private List errors = new ArrayList(); // a list of syntax errors produced by Antlr during parsing
19 |
20 | public List getErrors() {
21 | return errors;
22 | }
23 |
24 | public void setErrors(List errors) {
25 | this.errors = errors;
26 | }
27 |
28 | public boolean hasErrors() {
29 | return errors.size() > 0;
30 | }
31 |
32 | public Node getRootNode() {
33 | return root;
34 | }
35 |
36 | public void setRootNode(Node root) {
37 | this.root = root;
38 | }
39 |
40 | }
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/tree/extra/EQLOperator.java:
--------------------------------------------------------------------------------
1 | package org.iptc.extra.core.eql.tree.extra;
2 |
3 | import java.util.List;
4 |
5 | import org.iptc.extra.core.eql.tree.nodes.Clause;
6 | import org.iptc.extra.core.eql.tree.nodes.Modifier;
7 | import org.iptc.extra.core.eql.tree.nodes.Operator;
8 | import org.iptc.extra.core.eql.tree.nodes.PrefixClause;
9 |
10 | /**
11 | *
12 | * @author manos schinas
13 | *
14 | * A set of valid operators in EQL, built upon modified or, and, not, prox
15 | *
16 | */
17 | public enum EQLOperator {
18 | AND, // and
19 | OR, // or
20 | NOT, // not
21 | MINIMUM, // or/countunique>n
22 | DISTANCE, // prox/unit=word/distancen
24 | MINIMUM_OCCURRENCE, // or/count>n
25 | MAXIMUM_OCCURRENCE, // or/count>n
26 | ORDER, // prox/ordered
27 | SENTENCE, // prox/unit=sentence/distance=1
28 | PARAGRAPH, // prox/unit=paragraph/distance=1
29 | NOT_IN_PHRASE, // prox/unit=word/distance>0
30 | NOT_IN_SENTENCE, // prox/unit=sentence/distance>1
31 | NOT_IN_PARAGRAPH, // prox/unit=paragraph/distance>1
32 | ORDER_AND_DISTANCE, // prox/unit=word/distance modifiers = operator.getModifiers();
42 | String op = operator.getOperator();
43 | if(op == null) {
44 | return null;
45 | }
46 |
47 | if(op.equals("and")) {
48 | if(modifiers == null || modifiers.isEmpty()) {
49 | return AND;
50 | }
51 | }
52 | else if (op.equals("or")) {
53 | if(modifiers == null || modifiers.isEmpty()) {
54 | return OR;
55 | }
56 | else {
57 | if(operator.hasModifier("count")) {
58 | Modifier modifier = operator.getModifier("count");
59 | if(modifier.isComparitorGT() || modifier.isComparitorGTE()) {
60 | return MINIMUM_OCCURRENCE;
61 | }
62 |
63 | if(modifier.isComparitorLT() || modifier.isComparitorLTE()) {
64 | return MAXIMUM_OCCURRENCE;
65 | }
66 | }
67 | if(operator.hasModifier("countunique")) {
68 | Modifier modifier = operator.getModifier("countunique");
69 | if(modifier.isComparitorGT() || modifier.isComparitorGTE()) {
70 | return MINIMUM;
71 | }
72 | }
73 | }
74 | }
75 | else if(op.equals("not")) {
76 | return NOT;
77 | }
78 | else if(op.equals("prox")) {
79 | if(modifiers != null && !modifiers.isEmpty()) {
80 | if(operator.hasModifier("distance") && operator.hasModifier("unit")) {
81 | Modifier distanceModifier = operator.getModifier("distance");
82 | Modifier unitModifier = operator.getModifier("unit");
83 |
84 | // word-level distances
85 | if(unitModifier.isComparitorEQ() && unitModifier.valueEquals("word")) {
86 | if(distanceModifier.isComparitorLTE() || distanceModifier.isComparitorLT()) {
87 | if(operator.hasModifier("ordered")) {
88 | return ORDER_AND_DISTANCE;
89 | }
90 | return DISTANCE;
91 | }
92 |
93 | if(distanceModifier.isComparitorGT()) {
94 | if(distanceModifier.valueEquals("0")) {
95 | return NOT_IN_PHRASE;
96 | }
97 |
98 | return NOT_WITHIN_DISTANCE;
99 | }
100 | }
101 |
102 | // sentence-level distances
103 | if(unitModifier.isComparitorEQ() && unitModifier.valueEquals("sentence")) {
104 | if(distanceModifier.isComparitorEQ() && distanceModifier.valueEquals("1")) {
105 | return SENTENCE;
106 | }
107 | if(distanceModifier.isComparitorGT()) {
108 | return NOT_IN_SENTENCE;
109 | }
110 | }
111 |
112 | // paragraph-level distances
113 | if(unitModifier.isComparitorEQ() && unitModifier.valueEquals("paragraph")) {
114 | if(distanceModifier.isComparitorEQ() && distanceModifier.valueEquals("1")) {
115 | return PARAGRAPH;
116 | }
117 | if(distanceModifier.isComparitorGT()) {
118 | return NOT_IN_PARAGRAPH;
119 | }
120 | }
121 |
122 | }
123 | if(operator.hasModifier("ordered")) {
124 | return ORDER;
125 | }
126 |
127 | }
128 | }
129 |
130 | return null;
131 | }
132 |
133 | public static boolean isValid(Operator operator) {
134 | EQLOperator extraOperator = getEQLOperator(operator);
135 | return extraOperator != null;
136 | }
137 |
138 | public static boolean isEQLOperatorClause(Clause clause, EQLOperator extraOperator) {
139 | if(clause instanceof PrefixClause) {
140 | PrefixClause prefixClause = (PrefixClause) clause;
141 | if(prefixClause.getEQLOperator() != null && prefixClause.getEQLOperator() == extraOperator) {
142 | return true;
143 | }
144 | }
145 | return false;
146 | }
147 |
148 | public static boolean isWordDistanceOperator(EQLOperator extraOperator) {
149 | return (extraOperator == EQLOperator.DISTANCE || extraOperator == EQLOperator.NOT_WITHIN_DISTANCE ||
150 | extraOperator == EQLOperator.ORDER || extraOperator == EQLOperator.ORDER_AND_DISTANCE ||
151 | extraOperator == EQLOperator.NOT_IN_PHRASE);
152 | }
153 | }
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/tree/extra/EQLRelation.java:
--------------------------------------------------------------------------------
1 | package org.iptc.extra.core.eql.tree.extra;
2 |
3 | import org.iptc.extra.core.eql.tree.nodes.Relation;
4 |
5 | public enum EQLRelation {
6 |
7 | CONTAIN ("="),
8 | EXACT ("=="),
9 | NOT_EQUAL ("<>"),
10 | LT ("<"),
11 | LTE ("<="),
12 | GT (">"),
13 | GTE (">="),
14 | ADJ ("adj"),
15 | ALL ("all"),
16 | ANY ("any"),
17 | WITHIN ("within");
18 |
19 | private final String relation;
20 |
21 | EQLRelation(String relation) {
22 | this.relation = relation;
23 | };
24 |
25 | private String relation() { return relation; }
26 |
27 | public static boolean isValid(Relation relation) {
28 | String r = relation.getRelation();
29 | if(r == null) {
30 | return false;
31 | }
32 |
33 | r = r.toLowerCase();
34 |
35 | if(r.equals(CONTAIN.relation())) {
36 | return true;
37 | }
38 | if(r.equals(EXACT.relation())) {
39 | return true;
40 | }
41 | if(r.equals(NOT_EQUAL.relation())) {
42 | return true;
43 | }
44 | if(r.equals(LT.relation()) || r.equals(LTE.relation())) {
45 | return true;
46 | }
47 | if(r.equals(GT.relation()) || r.equals(GTE.relation())) {
48 | return true;
49 | }
50 | if(r.equals(ADJ.relation())) {
51 | return true;
52 | }
53 | if(r.equals(ALL.relation())) {
54 | return true;
55 | }
56 | if(r.equals(ANY.relation())) {
57 | return true;
58 | }
59 | if(r.equals(WITHIN.relation())) {
60 | return true;
61 | }
62 |
63 | return false;
64 | }
65 | }
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/tree/nodes/Clause.java:
--------------------------------------------------------------------------------
1 | package org.iptc.extra.core.eql.tree.nodes;
2 |
3 | /**
4 | *
5 | * @author manos schinas
6 | *
7 | * Clause class corresponds to a statement in an EQL query
8 | *
9 | * Can be one of the following:
10 | * prefix clause (org.iptc.extra.core.eql.tree.nodes.PrefixClause)
11 | * search clause (org.iptc.extra.core.eql.tree.nodes.SearchClause)
12 | * comment clause (org.iptc.extra.core.eql.tree.nodes.CommentClause)
13 | * reference clause (org.iptc.extra.core.eql.tree.nodes.ReferenceClause)
14 | *
15 | */
16 | public class Clause extends Node {
17 |
18 | @Override
19 | public boolean hasChildren() {
20 | return true;
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/tree/nodes/CommentClause.java:
--------------------------------------------------------------------------------
1 | package org.iptc.extra.core.eql.tree.nodes;
2 |
3 | /**
4 | * @author manosetro - Manos Schinas
5 | *
6 | * CommentClause corresponds to in-line comments in the rule.
7 | * Every string between //...// is considered as a comment and mapped to a CommentClause object in the syntax tree.
8 | *
9 | * (or
10 | * (title any "term1 term2 term2")
11 | * //this is a comment that will be mapped to a ReferenceClause//
12 | * )
13 | *
14 | */
15 | public class CommentClause extends Clause {
16 |
17 | protected String comment;
18 |
19 | public CommentClause() {
20 |
21 | }
22 |
23 | public CommentClause(String comment) {
24 | this.comment = comment;
25 | }
26 |
27 | public String getComment() {
28 | return comment;
29 | }
30 |
31 | public void setComment(String comment) {
32 | this.comment = comment;
33 | }
34 |
35 | @Override
36 | public boolean hasChildren() {
37 | return false;
38 | }
39 |
40 | @Override
41 | public String toString() {
42 | return comment;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/tree/nodes/ErrorMessageNode.java:
--------------------------------------------------------------------------------
1 | package org.iptc.extra.core.eql.tree.nodes;
2 |
3 | /**
4 | *
5 | * @author manos schinas
6 | *
7 | * A node that corresponds to a syntax error produced during eql parsing by antlr
8 | *
9 | */
10 | public class ErrorMessageNode extends Node {
11 |
12 | private String errorMessage;
13 |
14 | @Override
15 | public boolean hasChildren() {
16 | return false;
17 | }
18 |
19 | public String getErrorMessage() {
20 | return errorMessage;
21 | }
22 |
23 | public void setErrorMessage(String errorMessage) {
24 | this.errorMessage = errorMessage;
25 | }
26 |
27 | public String toString() {
28 | return errorMessage;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/tree/nodes/Index.java:
--------------------------------------------------------------------------------
1 | package org.iptc.extra.core.eql.tree.nodes;
2 |
3 | /**
4 | *
5 | * @author manos schinas
6 | *
7 | * Part of search clauses:
8 | *
9 | * Index Relation SearchTerm
10 | * | | |
11 | * title any "term1 term2"
12 | *
13 | */
14 | public class Index extends Node {
15 |
16 | private String name;
17 |
18 | public Index(String name) {
19 | super();
20 | this.name = name;
21 | }
22 |
23 | public String getName() {
24 | return name;
25 | }
26 |
27 | public void setName(String name) {
28 | this.name = name;
29 | }
30 |
31 | @Override
32 | public boolean hasChildren() {
33 | return false;
34 | }
35 |
36 | @Override
37 | public String toString() {
38 | return name;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/tree/nodes/Modified.java:
--------------------------------------------------------------------------------
1 | package org.iptc.extra.core.eql.tree.nodes;
2 |
3 | import java.util.ArrayList;
4 | import java.util.HashMap;
5 | import java.util.List;
6 | import java.util.Map;
7 |
8 | /**
9 | *
10 | * @author manos schinas
11 | *
12 | * Modified class represents nodes in the syntax tree that can be modified.
13 | * There are two objects that can be modified: operators (org.iptc.extra.core.eql.tree.nodes.Operator)
14 | * and relations (org.iptc.extra.core.eql.tree.nodes.Relation)
15 | *
16 | *
17 | * Example: prox/unit=word/distance>2
18 | * Operator prox is modified by two modifiers unit=word and distance>2
19 | * These two modifiers are kept into modifiersMap
20 | *
21 | */
22 | public class Modified extends Node {
23 |
24 | protected Map modifiersMap = new HashMap();
25 |
26 | public List getModifiers() {
27 | return new ArrayList(modifiersMap.values());
28 | }
29 |
30 | public void setModifiers(List modifiers) {
31 | for(Modifier modifier : modifiers) {
32 | modifiersMap.put(modifier.getModifier(), modifier);
33 | }
34 | }
35 |
36 | public boolean hasModifier(String modifier) {
37 | return modifiersMap.containsKey(modifier);
38 | }
39 |
40 | public Modifier getModifier(String modifier) {
41 | return modifiersMap.get(modifier);
42 | }
43 |
44 | @Override
45 | public boolean hasChildren() {
46 | return false;
47 | }
48 |
49 | public boolean isModified() {
50 | return !modifiersMap.isEmpty();
51 | }
52 |
53 | @Override
54 | public String toString() {
55 | StringBuffer buffer = new StringBuffer();
56 | for(Modifier modifier : modifiersMap.values()) {
57 | buffer.append(modifier);
58 | }
59 |
60 | return buffer.toString();
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/tree/nodes/Modifier.java:
--------------------------------------------------------------------------------
1 | package org.iptc.extra.core.eql.tree.nodes;
2 |
3 | /**
4 | *
5 | * @author manos schinas
6 | *
7 | * A modifier has the following syntax in EQL
8 | * /modifier comparitor value
9 | *
10 | * Example: or/count>5
11 | *
12 | * Node or is modified by count>5
13 | * modifier: count
14 | * comparitor: >
15 | * value: 5
16 | *
17 | */
18 | public class Modifier extends Node {
19 |
20 | private String modifier;
21 | private String comparitor;
22 | private String value;
23 |
24 | public Modifier(String modifier) {
25 | this.modifier = modifier;
26 | }
27 |
28 | public Modifier(String modifier, String comparitor, String value) {
29 | this.modifier = modifier;
30 | this.comparitor = comparitor;
31 | this.value = value;
32 | }
33 |
34 | public String getModifier() {
35 | return modifier;
36 | }
37 |
38 | public void setModifier(String modifier) {
39 | this.modifier = modifier;
40 | }
41 |
42 | public String getComparitor() {
43 | return comparitor;
44 | }
45 |
46 | public void setComparitor(String comparitor) {
47 | this.comparitor = comparitor;
48 | }
49 |
50 | public String getValue() {
51 | return value;
52 | }
53 |
54 | public void setValue(String value) {
55 | this.value = value;
56 | }
57 |
58 | public boolean hasValue() {
59 | return (value != null && comparitor != null);
60 | }
61 |
62 | public boolean valueEquals(String value) {
63 | return value.equals(this.value);
64 | }
65 |
66 | public boolean isComparitorLT() {
67 | return hasValue() && comparitor.equals("<");
68 | }
69 |
70 | public boolean isComparitorLTE() {
71 | return hasValue() && comparitor.equals("<=");
72 | }
73 |
74 | public boolean isComparitorGT() {
75 | return hasValue() && comparitor.equals(">");
76 | }
77 |
78 | public boolean isComparitorGTE() {
79 | return hasValue() && comparitor.equals(">=");
80 | }
81 |
82 | public boolean isComparitorEQ() {
83 | return hasValue() && comparitor.equals("=");
84 | }
85 |
86 | @Override
87 | public String toString() {
88 | return "/" + (hasValue() ? modifier + comparitor + value : modifier);
89 | }
90 |
91 | @Override
92 | public boolean hasChildren() {
93 | return false;
94 | }
95 |
96 | }
97 |
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/tree/nodes/Node.java:
--------------------------------------------------------------------------------
1 | package org.iptc.extra.core.eql.tree.nodes;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | /**
7 | * @author manosetro
8 | *
9 | * Node class represents a single node in the syntax tree.
10 | * Node class is extended by any other class in the syntax tree.
11 | *
12 | */
13 | public abstract class Node {
14 |
15 | protected Node parent; // the parent node of the current node
16 |
17 | protected List children = new ArrayList(); // a list of children
18 |
19 | protected List errors = new ArrayList(); // a list of nodes, correspond to syntax error
20 |
21 | protected int depth; // the depth of that node in the syntax tree
22 |
23 | protected boolean valid = true; // indicates whether that node is valid or not
24 |
25 | public abstract boolean hasChildren();
26 |
27 | public Node getParent() {
28 | return parent;
29 | }
30 |
31 | public void setParent(Node parent) {
32 | this.parent = parent;
33 | }
34 |
35 | public int getDepth() {
36 | return depth;
37 | }
38 |
39 | public void setDepth(int depth) {
40 | this.depth = depth;
41 | }
42 |
43 | public int getChildCount() {
44 | return children.size();
45 | }
46 |
47 | public Node getChild(int index) {
48 | return children.get(index);
49 | }
50 |
51 | public boolean addError(Node error) {
52 | return errors.add(error);
53 | }
54 |
55 | public List getErrors() {
56 | return errors;
57 | }
58 |
59 | public Node getError(int index) {
60 | return errors.get(index);
61 | }
62 |
63 | public boolean isValid() {
64 | return valid;
65 | }
66 |
67 | public void setValid(boolean valid) {
68 | this.valid = valid;
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/tree/nodes/Operator.java:
--------------------------------------------------------------------------------
1 | package org.iptc.extra.core.eql.tree.nodes;
2 |
3 | /**
4 | *
5 | * @author manos schinas
6 | *
7 | * Operator node that can take 4 values: or, and, prox, not.
8 | *
9 | */
10 | public class Operator extends Modified {
11 |
12 | private String operator;
13 |
14 | public Operator(String operator) {
15 | this.operator = operator;
16 | }
17 |
18 | public String getOperator() {
19 | return operator;
20 | }
21 |
22 | public void setOperator(String operator) {
23 | this.operator = operator;
24 | }
25 |
26 | public boolean isOr() {
27 | return operator != null && operator.contentEquals("or");
28 | }
29 |
30 | public boolean isAnd() {
31 | return operator != null && operator.contentEquals("and");
32 | }
33 |
34 | public boolean isNot() {
35 | return operator != null && operator.contentEquals("not");
36 | }
37 |
38 | public boolean isProx() {
39 | return operator != null && operator.contentEquals("prox");
40 | }
41 |
42 | @Override
43 | public String toString() {
44 | StringBuffer buffer = new StringBuffer();
45 | buffer.append(operator);
46 | buffer.append(super.toString());
47 | return buffer.toString();
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/tree/nodes/PrefixClause.java:
--------------------------------------------------------------------------------
1 | package org.iptc.extra.core.eql.tree.nodes;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import org.apache.commons.lang3.StringUtils;
7 | import org.iptc.extra.core.eql.tree.extra.EQLOperator;
8 |
9 | /**
10 | *
11 | * @author manos schinas
12 | *
13 | * PreficClause corresponds to a statement that combines other statements with one of the four operators.
14 | *
15 | * PreficClause <- (Operator
16 | * Clause+
17 | * )
18 | *
19 | *
20 | *
21 | */
22 | public class PrefixClause extends Clause {
23 |
24 | private Operator operator; // the operator: or, and, not, prox
25 | private EQLOperator eqlOperator; // the EQL operator
26 |
27 | private List clauses = new ArrayList(); // the sub-clauses of that prefix clause
28 |
29 | public Operator getOperator() {
30 | return operator;
31 | }
32 |
33 | public void setOperator(Operator operator) {
34 | this.operator = operator;
35 | children.add(operator);
36 | }
37 |
38 | public EQLOperator getEQLOperator() {
39 | return eqlOperator;
40 | }
41 |
42 | public void setEQLOperator(EQLOperator validOperator) {
43 | this.eqlOperator = validOperator;
44 | }
45 |
46 | public List getClauses() {
47 | return clauses;
48 | }
49 |
50 | public Clause getClause(int index) {
51 | return clauses.get(index);
52 | }
53 |
54 | /*
55 | * Get only search clauses
56 | */
57 | public List getSearchClause() {
58 | List searchClauses = new ArrayList();
59 | for(Clause clause : clauses) {
60 | if(clause instanceof SearchClause) {
61 | searchClauses.add((SearchClause) clause);
62 | }
63 | }
64 | return searchClauses;
65 | }
66 |
67 | /*
68 | * Get prefix or search clauses
69 | */
70 | public List getPrefixOrSearchClause() {
71 | List validClauses = new ArrayList();
72 | for(Clause clause : clauses) {
73 | if(clause instanceof SearchClause || clause instanceof PrefixClause) {
74 | validClauses.add(clause);
75 | }
76 | }
77 | return validClauses;
78 | }
79 |
80 | public void setClauses(List clauses) {
81 | this.clauses = clauses;
82 |
83 | children.addAll(clauses);
84 | }
85 |
86 | @Override
87 | public boolean hasChildren() {
88 | return true;
89 | }
90 |
91 | @Override
92 | public String toString() {
93 | StringBuffer buffer = new StringBuffer();
94 | buffer.append("(");
95 | buffer.append(operator);
96 | buffer.append(" ");
97 | buffer.append(StringUtils.join(clauses, " "));
98 | buffer.append(")");
99 | return buffer.toString();
100 | }
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/tree/nodes/ReferenceClause.java:
--------------------------------------------------------------------------------
1 | package org.iptc.extra.core.eql.tree.nodes;
2 |
3 | import org.iptc.extra.core.eql.tree.SyntaxTree;
4 | import org.iptc.extra.core.types.Rule;
5 |
6 | /**
7 | *
8 | * @author manos schinas
9 | *
10 | * A reference clause corresponds to another referenced rule
11 | *
12 | * Example:
13 | * (or
14 | * (title any "term1 term2")
15 | * (@ref == 5954d2231bac0c2f382b7ca5)
16 | * )
17 | *
18 | */
19 | public class ReferenceClause extends Clause {
20 |
21 | private String ruleId; // the id of the referenced rule
22 |
23 | private Rule rule; // the referenced rule
24 |
25 | private SyntaxTree ruleSyntaxTree; // the syntax tree of the referenced rule
26 |
27 |
28 | public String getRuleId() {
29 | return ruleId;
30 | }
31 |
32 | public void setRuleId(String ruleId) {
33 | this.ruleId = ruleId;
34 | }
35 |
36 | public Rule getRule() {
37 | return rule;
38 | }
39 |
40 | public void setRule(Rule rule) {
41 | this.rule = rule;
42 | }
43 |
44 | public SyntaxTree getRuleSyntaxTree() {
45 | return ruleSyntaxTree;
46 | }
47 |
48 | public void setRuleSyntaxTree(SyntaxTree ruleSyntaxTree) {
49 | this.ruleSyntaxTree = ruleSyntaxTree;
50 | }
51 |
52 | @Override
53 | public String toString() {
54 | return "( @ref == " + ruleId + " )";
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/tree/nodes/Relation.java:
--------------------------------------------------------------------------------
1 | package org.iptc.extra.core.eql.tree.nodes;
2 |
3 | /**
4 | *
5 | * @author manos schinas
6 | *
7 | * Part of search clauses:
8 | *
9 | * Index Relation SearchTerm
10 | * | | |
11 | * title any "term1 term2"
12 | *
13 | */
14 | public class Relation extends Modified {
15 |
16 | private String relation;
17 |
18 | public Relation(String relation) {
19 | this.relation = relation;
20 | }
21 |
22 | public String getRelation() {
23 | return relation;
24 | }
25 |
26 | public void setRelation(String relation) {
27 | this.relation = relation;
28 | }
29 |
30 | @Override
31 | public boolean hasChildren() {
32 | return false;
33 | }
34 |
35 | public boolean is(String relation) {
36 | return this.relation.equals(relation);
37 | }
38 |
39 | @Override
40 | public String toString() {
41 | StringBuffer buffer = new StringBuffer();
42 | buffer.append(relation);
43 | buffer.append(super.toString());
44 | return buffer.toString();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/tree/nodes/SearchClause.java:
--------------------------------------------------------------------------------
1 | package org.iptc.extra.core.eql.tree.nodes;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | /**
7 | *
8 | * @author manos schinas
9 | *
10 | * A search clause consists of
11 | * Index Relation SearchTerm
12 | *
13 | * Index and Relation are optional parts.
14 | * When messing SearchTerm can match any field.
15 | *
16 | */
17 | public class SearchClause extends Clause {
18 |
19 | private Index index;
20 |
21 | private Relation relation;
22 |
23 | private SearchTerm searchTerm;
24 |
25 | public SearchClause() {
26 |
27 | }
28 |
29 | public SearchClause(SearchTerm searchTerm) {
30 | super();
31 | this.searchTerm = searchTerm;
32 |
33 | children.add(searchTerm);
34 | }
35 |
36 | public SearchClause(Index index, Relation relation, SearchTerm searchTerm) {
37 | super();
38 | this.index = index;
39 | this.relation = relation;
40 | this.searchTerm = searchTerm;
41 |
42 | children.add(index);
43 | children.add(relation);
44 | children.add(searchTerm);
45 | }
46 |
47 | public Index getIndex() {
48 | return index;
49 | }
50 |
51 | public void setIndex(Index index) {
52 | this.index = index;
53 | children.add(index);
54 | }
55 |
56 | public Relation getRelation() {
57 | return relation;
58 | }
59 |
60 | public void setRelation(Relation relation) {
61 | this.relation = relation;
62 | children.add(relation);
63 | }
64 |
65 | public SearchTerm getSearchTerm() {
66 | return searchTerm;
67 | }
68 |
69 | public void setSearchTerm(SearchTerm searchTerm) {
70 | this.searchTerm = searchTerm;
71 | children.add(searchTerm);
72 | }
73 |
74 | public boolean hasIndex() {
75 | return (index != null && relation != null);
76 | }
77 |
78 | @Override
79 | public String toString() {
80 | return (hasIndex() ? index + " " + relation + " " : "") + searchTerm.toString();
81 | }
82 |
83 | public List splitSearchClause() {
84 | List searchClauses = new ArrayList();
85 |
86 | for(String term : searchTerm.getTerms()) {
87 | SearchTerm st = new SearchTerm(term);
88 | SearchClause searchClause = new SearchClause(index, relation, st);
89 |
90 | searchClauses.add(searchClause);
91 | }
92 |
93 | return searchClauses;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/tree/nodes/SearchTerm.java:
--------------------------------------------------------------------------------
1 | package org.iptc.extra.core.eql.tree.nodes;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import org.apache.commons.lang3.StringUtils;
7 |
8 | /**
9 | *
10 | * @author manos schinas
11 | *
12 | * Part of search clauses:
13 | *
14 | * Index Relation SearchTerm
15 | * | | |
16 | * title any "term1 term2"
17 | * title any "\$[0-9]\s+(million|billion)?"
18 | */
19 | public class SearchTerm extends Node {
20 |
21 | // regular expression characters
22 | private static String[] regexpCharacters = {".", "+", "|", "{", "}", "[", "]", "(", ")", "\"", "\\"};
23 |
24 | // wildcard characters
25 | private static String[] wildcards = {"+", "*"};
26 |
27 | private List terms = new ArrayList(); // terms of the search term
28 |
29 | public SearchTerm() {
30 |
31 | }
32 |
33 | public SearchTerm(String term) {
34 | this.terms.add(term);
35 | }
36 |
37 | public SearchTerm(List terms) {
38 | this.terms.addAll(terms);
39 | }
40 |
41 | public List getTerms() {
42 | return terms;
43 | }
44 |
45 | public void setTerms(List terms) {
46 | this.terms = terms;
47 | }
48 |
49 | /*
50 | * Returns a representation of search term by concatenating single terms
51 | */
52 | public String getSearchTerm() {
53 | String searchTerm = StringUtils.join(terms, " ");
54 | if(searchTerm != null && searchTerm.contains("/")) {
55 | searchTerm = searchTerm.replaceAll(" / ", "/");
56 | }
57 |
58 | return searchTerm;
59 | }
60 |
61 | /*
62 | * Returns a representation of search term by concatenating single terms
63 | */
64 | public String getQueryString(String prefix) {
65 | List queryParts = new ArrayList();
66 | for(String term : terms) {
67 | if(isRegexp(term)) {
68 | queryParts.add(prefix + "(/" + term + "/)");
69 | }
70 | else {
71 | if(term.matches(".+\\s+.+")) {
72 | List termParts = new ArrayList();
73 | for(String termPart : term.split("\\s+")) {
74 | termParts.add("+" + termPart);
75 | }
76 | term = "(" + StringUtils.join(termParts, " ") +")";
77 | }
78 | queryParts.add(prefix + term);
79 | }
80 | }
81 |
82 | return StringUtils.join(queryParts, " ");
83 | }
84 |
85 | @Override
86 | public boolean hasChildren() {
87 | return false;
88 | }
89 |
90 | @Override
91 | public String toString() {
92 | if(isRegexp()) {
93 | return "\"" + getRegexp(true) + "\"";
94 | }
95 |
96 | return "\"" + getSearchTerm() + "\"";
97 | }
98 |
99 | public int numberOfTerms() {
100 | return terms.size();
101 | }
102 |
103 | public String getTerm(int index) {
104 | return terms.get(index);
105 | }
106 |
107 | // Does search term contains wild-cards?
108 | public boolean hasWildCards() {
109 | for(String term : terms) {
110 | boolean has = hasWildCards(term);
111 | if(has) {
112 | return true;
113 | }
114 | }
115 | return false;
116 | }
117 |
118 | private boolean hasWildCards(String term) {
119 | for(String regexCharacter : wildcards) {
120 | if(term.contains(regexCharacter)) {
121 | return true;
122 | }
123 | }
124 |
125 | return false;
126 | }
127 |
128 | // Is search term a regular expression?
129 | public boolean isRegexp() {
130 | for(String term : terms) {
131 | boolean isRegex = isRegexp(term);
132 | if(isRegex) {
133 | return true;
134 | }
135 | }
136 | return false;
137 | }
138 |
139 | private boolean isRegexp(String term) {
140 | for(String regexCharacter : regexpCharacters) {
141 | if(term.contains(regexCharacter)) {
142 | return true;
143 | }
144 | }
145 |
146 | return false;
147 | }
148 |
149 | // check whether the regular expression contains a whitespace character
150 | public boolean doesRegexpContainWhitespaces() {
151 | String regexp = StringUtils.join(terms, "");
152 | if(regexp.contains("\\s")) {
153 | return true;
154 | }
155 |
156 | return false;
157 | }
158 |
159 | // Get regular expression string
160 | public String getRegexp(boolean predefinedCharacterClasses) {
161 | String regexp = StringUtils.join(terms, "");
162 |
163 | if(!predefinedCharacterClasses && regexp != null) {
164 | regexp = regexp
165 | .replace("\\d", "[0-9]")
166 | .replace("\\D", "[^0-9]")
167 | .replace("\\s", "[ \\t\\n\\x0B\\f\\r]")
168 | .replace("\\S", "[^ \\t\\n\\x0B\\f\\r]")
169 | .replace("\\w", "[a-zA-Z_0-9]")
170 | .replace("\\w", "[^a-zA-Z_0-9]");
171 |
172 | regexp = ".*" + regexp + ".*";
173 | }
174 |
175 | return regexp;
176 | }
177 |
178 | }
179 |
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/tree/utils/TreeUtils.java:
--------------------------------------------------------------------------------
1 | package org.iptc.extra.core.eql.tree.utils;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collection;
5 | import java.util.HashMap;
6 | import java.util.HashSet;
7 | import java.util.List;
8 | import java.util.Map;
9 | import java.util.Set;
10 |
11 | import org.iptc.extra.core.eql.EQLParser;
12 | import org.iptc.extra.core.eql.tree.SyntaxTree;
13 | import org.iptc.extra.core.eql.tree.nodes.Clause;
14 | import org.iptc.extra.core.eql.tree.nodes.CommentClause;
15 | import org.iptc.extra.core.eql.tree.nodes.Index;
16 | import org.iptc.extra.core.eql.tree.nodes.Node;
17 | import org.iptc.extra.core.eql.tree.nodes.Operator;
18 | import org.iptc.extra.core.eql.tree.nodes.PrefixClause;
19 | import org.iptc.extra.core.eql.tree.nodes.ReferenceClause;
20 | import org.iptc.extra.core.eql.tree.nodes.Relation;
21 | import org.iptc.extra.core.eql.tree.nodes.SearchClause;
22 | import org.iptc.extra.core.eql.tree.nodes.SearchTerm;
23 | import org.iptc.extra.core.eql.tree.visitor.SyntaxTreeVisitor;
24 | import org.iptc.extra.core.types.Rule;
25 | import org.iptc.extra.core.types.Schema;
26 |
27 | import edu.stanford.nlp.util.StringUtils;
28 |
29 | /**
30 | *
31 | * @author manos schinas
32 | *
33 | * This class contains a set of static methods, for the processing of syntax true produced by EQL parser
34 | *
35 | */
36 | public class TreeUtils {
37 |
38 | // Checks whether the tree starting to root node is valid
39 | public static boolean isTreeValid(Node root) {
40 | // iterate over relation nodes. Return false (rule is invalid) if any relation is invalid
41 | for(Relation relation : getRelations(root)) {
42 | if(!relation.isValid()) {
43 | return false;
44 | }
45 | }
46 |
47 | // iterate over operator nodes. Return false (rule is invalid) if any operator is invalid
48 | for(Operator operator : getOperators(root)) {
49 | if(!operator.isValid()) {
50 | return false;
51 | }
52 | }
53 | return true;
54 | }
55 |
56 | // Iterate over relation and operator nodes and return any invalid node
57 | public static List getInvalidNodes(Node root) {
58 | List nodes = new ArrayList();
59 | for(Relation relation : getRelations(root)) {
60 | if(!relation.isValid()) {
61 | nodes.add(relation);
62 | }
63 | }
64 | for(Operator operator : getOperators(root)) {
65 | if(!operator.isValid()) {
66 | nodes.add(operator);
67 | }
68 | }
69 |
70 | return nodes;
71 | }
72 |
73 | /*
74 | * checks whether the tree, starting at root, matches the given schema. Returns invalid fields/indices
75 | */
76 | public static Set validateSchema(Node root, Schema schema) {
77 | SyntaxTreeVisitor> visitor = new SyntaxTreeVisitor>() {
78 | public Set visitIndex(Index index) {
79 | Set indices = new HashSet();
80 |
81 | if(!schema.getFieldNames().contains(index.getName()) && !"text_content".equals(index.getName())) {
82 | index.setValid(false);
83 | indices.add(index.getName());
84 | }
85 | return indices;
86 | }
87 |
88 | protected Set aggregateResult(Set aggregate, Set nextResult) {
89 | aggregate.addAll(nextResult);
90 | return aggregate;
91 | }
92 |
93 | protected Set defaultResult() {
94 | return new HashSet();
95 | }
96 | };
97 |
98 | if(schema == null) {
99 | return new HashSet();
100 | }
101 |
102 | Set indices = visitor.visit(root);
103 | return indices;
104 |
105 | }
106 |
107 | /*
108 | * Validate tree across multiple schemas
109 | */
110 | public static Map> validateSchema(Node root, List schemas) {
111 |
112 | Map> map = new HashMap>();
113 | for(Schema schema : schemas) {
114 | Set fields = schema.getFieldNames();
115 | Set indices = getIndices(root);
116 |
117 | indices.retainAll(fields);
118 |
119 | map.put(schema.getId(), indices);
120 | }
121 |
122 | return map;
123 | }
124 |
125 | /*
126 | * Traverse the tree and returns a set of indices
127 | */
128 | public static Set getIndices(Node root) {
129 | SyntaxTreeVisitor> visitor = new SyntaxTreeVisitor>() {
130 |
131 | public Set visitIndex(Index index) {
132 | Set indices = new HashSet();
133 | indices.add(index.getName());
134 |
135 | return indices;
136 | }
137 |
138 | @Override
139 | protected Set aggregateResult(Set aggregate, Set nextResult) {
140 | aggregate.addAll(nextResult);
141 | return aggregate;
142 | }
143 |
144 | @Override
145 | protected Set defaultResult() {
146 | return new HashSet();
147 | }
148 |
149 | };
150 |
151 | Set indices = visitor.visit(root);
152 | return indices;
153 | }
154 |
155 | /*
156 | * Traverse the tree and returns a set of relation nodes
157 | */
158 | public static Set getRelations(Node root) {
159 | SyntaxTreeVisitor> visitor = new SyntaxTreeVisitor>() {
160 | public Set visitRelation(Relation relation) {
161 | Set relations = new HashSet();
162 | relations.add(relation);
163 |
164 | return relations;
165 | }
166 |
167 | protected Set aggregateResult(Set aggregate, Set nextResult) {
168 | aggregate.addAll(nextResult);
169 | return aggregate;
170 | }
171 |
172 | protected Set defaultResult() {
173 | return new HashSet();
174 | }
175 | };
176 |
177 | Set relations = visitor.visit(root);
178 | return relations;
179 | }
180 |
181 | /*
182 | * Traverse the tree and returns a set of nodes, corresponding to search clauses:
183 | *
184 | * SearchClause: Index Relation SearchTerm
185 | *
186 | * e.g. title any "term1 term2"
187 | *
188 | */
189 | public static Set getSearchClauses(Node root) {
190 | SyntaxTreeVisitor> visitor = new SyntaxTreeVisitor>() {
191 | public Set visitSearchClause(SearchClause searchClause) {
192 | Set searchClauses = new HashSet();
193 | searchClauses.add(searchClause);
194 |
195 | return searchClauses;
196 | }
197 |
198 | protected Set aggregateResult(Set aggregate, Set nextResult) {
199 | aggregate.addAll(nextResult);
200 | return aggregate;
201 | }
202 |
203 | protected Set defaultResult() {
204 | return new HashSet();
205 | }
206 | };
207 |
208 | Set searchClauses = visitor.visit(root);
209 | return searchClauses;
210 | }
211 |
212 | /*
213 | * Traverse the tree and returns a set of nodes, corresponding to search clauses having no idnex and relation
214 | *
215 | * e.g. "term1 term2"
216 | *
217 | */
218 | public static Set getSearchTermClauses(Node root) {
219 | SyntaxTreeVisitor> visitor = new SyntaxTreeVisitor>() {
220 | public Set visitSearchClause(SearchClause searchClause) {
221 | Set searchClauses = new HashSet();
222 |
223 | if(!searchClause.hasIndex()) {
224 | searchClauses.add(searchClause);
225 | }
226 |
227 | return searchClauses;
228 | }
229 |
230 | protected Set aggregateResult(Set aggregate, Set nextResult) {
231 | aggregate.addAll(nextResult);
232 | return aggregate;
233 | }
234 |
235 | protected Set defaultResult() {
236 | return new HashSet();
237 | }
238 | };
239 |
240 | Set searchClauses = visitor.visit(root);
241 | return searchClauses;
242 | }
243 |
244 | /*
245 | * Traverse the tree and returns a set of operator nodes
246 | */
247 | public static Set getOperators(Node root) {
248 | SyntaxTreeVisitor> visitor = new SyntaxTreeVisitor>() {
249 | public Set visitOperator(Operator operator) {
250 | Set operators = new HashSet();
251 | operators.add(operator);
252 |
253 | return operators;
254 | }
255 |
256 | protected Set aggregateResult(Set aggregate, Set nextResult) {
257 | aggregate.addAll(nextResult);
258 | return aggregate;
259 | }
260 |
261 | protected Set defaultResult() {
262 | return new HashSet();
263 | }
264 | };
265 |
266 | Set operators = visitor.visit(root);
267 | return operators;
268 | }
269 |
270 | /*
271 | * iterates over a list of clauses and checks whether all of them are search clauses
272 | */
273 | public static boolean areSearchClauses(Collection clauses) {
274 | for(Clause clause : clauses) {
275 | if(clause instanceof CommentClause) {
276 | continue;
277 | }
278 | if(clause instanceof PrefixClause) {
279 | return false;
280 | }
281 | }
282 | return true;
283 | }
284 |
285 | /*
286 | * iterates over a list of clauses and checks whether all of them are search clauses without index and relation
287 | */
288 | public static boolean areSearchTermClauses(Collection clauses) {
289 | for(Clause clause : clauses) {
290 | if(clause instanceof CommentClause) {
291 | continue;
292 | }
293 | if(clause instanceof PrefixClause) {
294 | return false;
295 | }
296 | if(clause instanceof ReferenceClause) {
297 | return false;
298 | }
299 | if(clause instanceof SearchClause) {
300 | SearchClause searchClause = (SearchClause) clause;
301 | if(searchClause.hasIndex()) {
302 | return false;
303 | }
304 | }
305 | }
306 | return true;
307 | }
308 |
309 | /*
310 | * Iterates over a list of clauses and create a SearchTerm produced by the concatenation of underlying SearchTerms.
311 | *
312 | * Clauses other than search clauses are ignored
313 | *
314 | */
315 | public static SearchTerm mergeSearchTerm(List clauses) {
316 | SearchTerm mergedSearchTerm = new SearchTerm();
317 |
318 | List mergedTerms = new ArrayList();
319 | for(Clause clause : clauses) {
320 | if(clause instanceof SearchClause) { // use only SearchClause statements
321 | SearchClause searchClause = (SearchClause) clause;
322 |
323 | SearchTerm searchTerms = searchClause.getSearchTerm();
324 | if(searchTerms.isRegexp()) {
325 | mergedTerms.add(searchTerms.getRegexp(false));
326 | }
327 | else {
328 | List terms = searchTerms.getTerms();
329 | mergedTerms.add(StringUtils.join(terms, " "));
330 | }
331 | }
332 | }
333 |
334 | mergedSearchTerm.setTerms(mergedTerms);
335 | return mergedSearchTerm;
336 | }
337 |
338 | /*
339 | * Traverse the tree and returns a set of index nodes
340 | */
341 | public static List getIndices(List searchClauses) {
342 | Set set = new HashSet();
343 | for(SearchClause searchClause : searchClauses) {
344 | if(searchClause.hasIndex()) {
345 | set.add(searchClause.getIndex().getName());
346 | }
347 | }
348 |
349 | return new ArrayList(set);
350 | }
351 |
352 | /*
353 | * Traverse the tree and aggregate reference clauses
354 | */
355 | public static List getReferences(Node root) {
356 | SyntaxTreeVisitor> visitor = new SyntaxTreeVisitor>() {
357 |
358 | public List visitReferenceClause(ReferenceClause reference) {
359 | List references = new ArrayList();
360 | references.add(reference);
361 |
362 | return references;
363 | }
364 |
365 | protected List aggregateResult(List aggregate, List nextResult) {
366 | aggregate.addAll(nextResult);
367 | return aggregate;
368 | }
369 |
370 | protected List defaultResult() {
371 | return new ArrayList();
372 | }
373 | };
374 |
375 | List references = visitor.visit(root);
376 | return references;
377 | }
378 |
379 | // validate a rule references inside another rule
380 | public static void validateReferenceRule(ReferenceClause reference, Schema schema) {
381 |
382 | Rule rule = reference.getRule();
383 |
384 | String query = rule.getQuery();
385 |
386 | SyntaxTree syntaxTree = EQLParser.parse(query);
387 | reference.setRuleSyntaxTree(syntaxTree);
388 |
389 | //Node root = syntaxTree.getRootNode();
390 |
391 | //List invalidNodes = ExtraValidator.validate(root, schema);
392 | //Set unmatchedIndices = TreeUtils.validateSchema(root, schema);
393 | //List references = TreeUtils.getReferences(root);
394 | }
395 |
396 | public static boolean isRuleValid(Rule rule, Schema schema) {
397 | String query = rule.getQuery();
398 |
399 | SyntaxTree syntaxTree = EQLParser.parse(query);
400 | Node root = syntaxTree.getRootNode();
401 |
402 | if(syntaxTree.hasErrors() || root == null) {
403 | return false;
404 | }
405 |
406 | return true;
407 |
408 | //List invalidNodes = ExtraValidator.validate(root, schema);
409 | //Set unmatchedIndices = TreeUtils.validateSchema(root, schema);
410 | //List references = TreeUtils.getReferences(root);
411 | }
412 |
413 | }
414 |
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/tree/visitor/EQL2HTMLVisitor.java:
--------------------------------------------------------------------------------
1 | package org.iptc.extra.core.eql.tree.visitor;
2 |
3 | import org.iptc.extra.core.eql.tree.nodes.Clause;
4 | import org.iptc.extra.core.eql.tree.nodes.CommentClause;
5 | import org.iptc.extra.core.eql.tree.nodes.Index;
6 | import org.iptc.extra.core.eql.tree.nodes.Operator;
7 | import org.iptc.extra.core.eql.tree.nodes.PrefixClause;
8 | import org.iptc.extra.core.eql.tree.nodes.ReferenceClause;
9 | import org.iptc.extra.core.eql.tree.nodes.Relation;
10 | import org.iptc.extra.core.eql.tree.nodes.SearchClause;
11 | import org.iptc.extra.core.eql.tree.nodes.SearchTerm;
12 |
13 | /**
14 | * @author manosetro - Manos Schinas
15 | *
16 | * EXTRA2ESQueryVisitor performs a depth-first traversal of the syntax tree and generates an HTML representation.
17 | *
18 | * Example rule:
19 | *
20 | * (or
21 | * (title any "term1 term2")
22 | * (and
23 | * (body adj "term3 term4")
24 | * (body any "term5")
25 | * )
26 | * )
27 | *
28 | * Transformed to the following HTML section:
29 | *
30 | * (
31 | *
or
32 | *
(
33 | *
title
34 | *
any
35 | *
"term1 term2"
36 | * )
37 | *
(
38 | *
and
39 | *
(
40 | *
body
41 | *
adj
42 | *
"term3 term4"
43 | * )
44 | *
(
45 | *
body
46 | *
any
47 | *
"term5"
48 | * )
49 | * )
50 | * )
51 | */
52 | public class EQL2HTMLVisitor extends SyntaxTreeVisitor {
53 |
54 | private String htmlTag;
55 |
56 | public EQL2HTMLVisitor(String htmlTag) {
57 | this.htmlTag = htmlTag;
58 | }
59 |
60 | @Override
61 | public String visitPrefixClause(PrefixClause prefixClause) {
62 | StringBuffer buffer = new StringBuffer();
63 |
64 | buffer.append("<" + htmlTag + " class=\"prefixClause\" data-depth=\"" + prefixClause.getDepth() + "\"" +
65 | (prefixClause.getEQLOperator()!=null ? " operator=\"" + prefixClause.getEQLOperator() + "\"" : "") +
66 | ">");
67 |
68 | buffer.append("(");
69 | buffer.append(visit(prefixClause.getOperator()));
70 |
71 | for(Clause clause : prefixClause.getClauses()) {
72 | buffer.append(visit(clause));
73 | }
74 |
75 | buffer.append(")");
76 | buffer.append("" + htmlTag + ">");
77 |
78 | return buffer.toString();
79 | }
80 |
81 | @Override
82 | public String visitOperator(Operator operator) {
83 | StringBuffer buffer = new StringBuffer();
84 |
85 | buffer.append("<" + htmlTag + " class=\"booleanOp\" data-valid=\"" + operator.isValid() + "\" "
86 | + "data-depth=\"" + operator.getDepth() + "\">");
87 | buffer.append(operator);
88 | buffer.append("" + htmlTag + ">");
89 |
90 | return buffer.toString();
91 | }
92 |
93 | @Override
94 | public String visitSearchClause(SearchClause searchClause) {
95 | StringBuffer buffer = new StringBuffer();
96 | buffer.append("<" + htmlTag + " class=\"searchClause\" data-depth=\"" + searchClause.getDepth() + "\">(");
97 |
98 | if(searchClause.hasIndex()) {
99 | buffer.append(visit(searchClause.getIndex()));
100 | buffer.append(visit(searchClause.getRelation()));
101 | }
102 | buffer.append(visit(searchClause.getSearchTerm()));
103 |
104 | buffer.append(") " + htmlTag + "> ");
105 | return buffer.toString();
106 | }
107 |
108 | @Override
109 | public String visitCommentClause(CommentClause commentClause) {
110 | return "";
111 | }
112 |
113 | @Override
114 | public String visitReferenceClause(ReferenceClause referenceClause) {
115 | StringBuffer buffer = new StringBuffer();
116 | buffer.append("<" + htmlTag + " data-depth=\"" + referenceClause.getDepth() + "\"> (");
117 |
118 | buffer.append("<" + htmlTag + " class=\"referenceClause\" rule=\"" + referenceClause.getRuleId() + "\""
119 | + "data-valid=\"" + referenceClause.isValid() + "\">");
120 |
121 | buffer.append("@ref == ");
122 | buffer.append(referenceClause.getRuleId());
123 |
124 | buffer.append("" + htmlTag + ">");
125 |
126 | buffer.append(") " + htmlTag + ">");
127 | return buffer.toString();
128 | }
129 |
130 | @Override
131 | public String visitIndex(Index index) {
132 | StringBuffer buffer = new StringBuffer();
133 |
134 | buffer.append("<" + htmlTag + " class=\"index\" data-valid=\"" + index.isValid() + "\" "
135 | + "data-depth=\"" + index.getDepth() + "\"> ");
136 | buffer.append(index.getName());
137 | buffer.append(" " + htmlTag + "> ");
138 |
139 | return buffer.toString();
140 | }
141 |
142 | @Override
143 | public String visitRelation(Relation relation) {
144 | StringBuffer buffer = new StringBuffer();
145 |
146 | buffer.append("<" + htmlTag + " class=\"relation\" data-valid=\"" + relation.isValid() + "\" "
147 | + "data-depth=\"" + relation.getDepth() + "\"> ");
148 | buffer.append(relation);
149 | buffer.append(" " + htmlTag + ">");
150 |
151 | return buffer.toString();
152 | }
153 |
154 | @Override
155 | public String visitSearchTerm(SearchTerm searchTerm) {
156 | StringBuffer buffer = new StringBuffer();
157 |
158 | buffer.append("<" + htmlTag + " class=\"searchTerm\" data-depth=\"" + searchTerm.getDepth() + "\"> ");
159 | buffer.append(searchTerm);
160 | buffer.append(" " + htmlTag + ">");
161 |
162 | return buffer.toString();
163 | }
164 | }
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/tree/visitor/EQL2HighlightVisitor.java:
--------------------------------------------------------------------------------
1 | package org.iptc.extra.core.eql.tree.visitor;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 | import java.util.Set;
6 | import java.util.stream.Collectors;
7 |
8 | import org.apache.commons.lang3.StringUtils;
9 | import org.elasticsearch.index.query.BoolQueryBuilder;
10 | import org.elasticsearch.index.query.MatchQueryBuilder;
11 | import org.elasticsearch.index.query.MultiMatchQueryBuilder;
12 | import org.elasticsearch.index.query.QueryBuilder;
13 | import org.elasticsearch.index.query.QueryStringQueryBuilder;
14 | import org.elasticsearch.index.query.RangeQueryBuilder;
15 | import org.iptc.extra.core.eql.tree.SyntaxTree;
16 | import org.iptc.extra.core.eql.tree.extra.EQLOperator;
17 | import org.iptc.extra.core.eql.tree.nodes.Clause;
18 | import org.iptc.extra.core.eql.tree.nodes.CommentClause;
19 | import org.iptc.extra.core.eql.tree.nodes.Index;
20 | import org.iptc.extra.core.eql.tree.nodes.PrefixClause;
21 | import org.iptc.extra.core.eql.tree.nodes.ReferenceClause;
22 | import org.iptc.extra.core.eql.tree.nodes.Relation;
23 | import org.iptc.extra.core.eql.tree.nodes.SearchClause;
24 | import org.iptc.extra.core.eql.tree.nodes.SearchTerm;
25 | import org.iptc.extra.core.types.Schema;
26 |
27 | import static org.elasticsearch.index.query.QueryBuilders.*;
28 |
29 | public class EQL2HighlightVisitor extends SyntaxTreeVisitor {
30 |
31 | private Schema schema;
32 |
33 | public EQL2HighlightVisitor(Schema schema) {
34 | this.schema = schema;
35 | }
36 |
37 | @Override
38 | public QueryBuilder visitPrefixClause(PrefixClause prefixClause) {
39 | EQLOperator extraOperator = prefixClause.getEQLOperator();
40 |
41 | if(extraOperator == null) {
42 | return null;
43 | }
44 |
45 | if(extraOperator == EQLOperator.AND) {
46 | return andToES(prefixClause);
47 | }
48 |
49 | if(extraOperator == EQLOperator.NOT_IN_PHRASE) {
50 | List clauses = prefixClause.getPrefixOrSearchClause();
51 | if(clauses.isEmpty()) {
52 | return null;
53 | }
54 |
55 | return visit(clauses.get(0));
56 | }
57 |
58 | return orToES(prefixClause);
59 | }
60 |
61 | private QueryBuilder andToES(PrefixClause prefixClause) {
62 | List childrenClauses = prefixClause.getClauses();
63 | childrenClauses = childrenClauses.stream().filter(clause -> !(clause instanceof CommentClause)).collect(Collectors.toList());
64 |
65 | if(childrenClauses.size() == 1) {
66 | return visit(childrenClauses.get(0));
67 | }
68 |
69 | BoolQueryBuilder booleanQb = boolQuery();
70 |
71 | List mustClauses = new ArrayList();
72 | List mustNotClauses = new ArrayList();
73 | for(Clause clause : childrenClauses) {
74 | if(EQLOperator.isEQLOperatorClause(clause, EQLOperator.NOT)) {
75 | mustNotClauses.addAll(getChildrenClausesQueries((PrefixClause) clause));
76 | }
77 | else {
78 | QueryBuilder queryBuilder = visit(clause);
79 | if(queryBuilder != null) {
80 | mustClauses.add(queryBuilder);
81 | }
82 | }
83 | }
84 |
85 | if(mustClauses.isEmpty()) {
86 | return null;
87 | }
88 |
89 | for(QueryBuilder stqb : mustClauses) {
90 | booleanQb.must(stqb);
91 | }
92 |
93 | for(QueryBuilder stqb : mustNotClauses) {
94 | booleanQb.mustNot(stqb);
95 | }
96 |
97 | return booleanQb;
98 | }
99 |
100 | private QueryBuilder orToES(PrefixClause prefixClause) {
101 |
102 | List childrenClauses = prefixClause.getClauses();
103 | childrenClauses = childrenClauses.stream().filter(clause -> !(clause instanceof CommentClause)).collect(Collectors.toList());
104 |
105 | if(childrenClauses.size() == 1) {
106 | return visit(childrenClauses.get(0));
107 | }
108 |
109 | BoolQueryBuilder booleanQb = boolQuery();
110 | List clausesQueries = getClausesQueries(childrenClauses);
111 | if(clausesQueries.isEmpty()) {
112 | return null;
113 | }
114 |
115 | for(QueryBuilder stqb : clausesQueries) {
116 | booleanQb.should(stqb);
117 | }
118 |
119 | return booleanQb;
120 | }
121 |
122 | private List getClausesQueries(List clauses) {
123 | List clausesQueries = new ArrayList();
124 | for(Clause clause : clauses) {
125 | QueryBuilder queryBuilder = visit(clause);
126 | if(queryBuilder == null) {
127 | continue;
128 | }
129 | clausesQueries.add(queryBuilder);
130 | }
131 | return clausesQueries;
132 | }
133 |
134 | private List getChildrenClausesQueries(PrefixClause prefixClause) {
135 | List childrenClauses = prefixClause.getClauses();
136 | List clausesQueries = new ArrayList();
137 | for(Clause clause : childrenClauses) {
138 | QueryBuilder queryBuilder = visit(clause);
139 | if(queryBuilder == null) {
140 | continue;
141 | }
142 | clausesQueries.add(queryBuilder);
143 | }
144 | return clausesQueries;
145 | }
146 |
147 | @Override
148 | public QueryBuilder visitSearchClause(SearchClause searchClause) {
149 | if(searchClause.hasIndex()) {
150 | Index index = searchClause.getIndex();
151 | Relation relation = searchClause.getRelation();
152 | SearchTerm searchTerm = searchClause.getSearchTerm();
153 |
154 | String indexName = index.getName();
155 | return searchClausetoES(indexName, relation, searchTerm);
156 | }
157 | else {
158 | QueryBuilder qb = visitChildren(searchClause);
159 | return qb;
160 | }
161 | }
162 |
163 | @Override
164 | public QueryBuilder visitReferenceClause(ReferenceClause referenceClause) {
165 | SyntaxTree syntaxTree = referenceClause.getRuleSyntaxTree();
166 | if(syntaxTree != null && !syntaxTree.hasErrors() && syntaxTree.getRootNode() != null) {
167 | return visit(syntaxTree.getRootNode());
168 | }
169 |
170 | return null;
171 | }
172 |
173 | private QueryBuilder searchClausetoES(String index, Relation relation, SearchTerm searchTerm) {
174 |
175 | if(index.equals("text_content")) {
176 | BoolQueryBuilder booleanQb = boolQuery();
177 | for(String field : schema.getTextualFieldNames()) {
178 | QueryBuilder fieldQb = searchClausetoES(field, relation, searchTerm);
179 | if(fieldQb != null) {
180 | booleanQb.should(fieldQb);
181 | }
182 | }
183 |
184 | return booleanQb;
185 | }
186 |
187 | boolean isRegexp = searchTerm.isRegexp();
188 | boolean hasWildcards = searchTerm.hasWildCards();
189 |
190 | String query = searchTerm.getSearchTerm();
191 | if(isRegexp && !relation.hasModifier("literal")) {
192 | return regexpSearchClause(index, relation, searchTerm);
193 | }
194 |
195 | if(hasWildcards && !relation.hasModifier("literal")) {
196 | return wildcardsSearchClause(index, relation, searchTerm);
197 | }
198 |
199 | if(relation.is("any") || relation.is("=")) {
200 | if(relation.hasModifier("stemming")) {
201 | index = "stemmed_" + index;
202 | }
203 | else if(relation.hasModifier("casesensitive")) {
204 | index = "case_sensitive_" + index;
205 | }
206 | else if(relation.hasModifier("literal")) {
207 | index = "literal_" + index;
208 | }
209 |
210 | return matchQuery(index, query);
211 |
212 | }
213 | else if (relation.is("==")) {
214 | return termQuery(index, query);
215 | }
216 | else if (relation.is("all")) {
217 | if(relation.hasModifier("stemming")) {
218 | index = "stemmed_" + index;
219 | }
220 | else if(relation.hasModifier("casesensitive")) {
221 | index = "case_sensitive_" + index;
222 | }
223 | else if(relation.hasModifier("literal")) {
224 | index = "literal_" + index;
225 | }
226 |
227 | MatchQueryBuilder queryBuilder = matchQuery(index, query);
228 | queryBuilder.operator(org.elasticsearch.index.query.Operator.AND);
229 |
230 | return queryBuilder;
231 | }
232 | else if (relation.is("adj")) {
233 | if(relation.hasModifier("stemming")) {
234 | index = "stemmed_" + index;
235 | }
236 | else if(relation.hasModifier("casesensitive")) {
237 | index = "case_sensitive_" + index;
238 | }
239 | else if(relation.hasModifier("literal")) {
240 | index = "literal_" + index;
241 | }
242 |
243 | return matchPhraseQuery(index, query);
244 | }
245 | else if(relation.is(">")) {
246 | RangeQueryBuilder qb = rangeQuery(index);
247 | return qb.gt(query);
248 | }
249 | else if(relation.is(">=")) {
250 | RangeQueryBuilder qb = rangeQuery(index);
251 | return qb.gte(query);
252 | }
253 | else if(relation.is("<")) {
254 | RangeQueryBuilder qb = rangeQuery(index);
255 | return qb.lt(query);
256 | }
257 | else if(relation.is("<=")) {
258 | RangeQueryBuilder qb = rangeQuery(index);
259 | return qb.lte(query);
260 | }
261 | else if(relation.is("within") && searchTerm.numberOfTerms() == 2) {
262 | RangeQueryBuilder qb = rangeQuery(index);
263 | return qb.gte(searchTerm.getTerm(0)).lte(searchTerm.getTerm(1));
264 | }
265 |
266 | return null;
267 | }
268 |
269 | private QueryBuilder wildcardsSearchClause(String index, Relation relation, SearchTerm searchTerm) {
270 |
271 | String query = searchTerm.getSearchTerm();
272 |
273 | if(relation.is("any") || relation.is("=") || relation.is("all") || relation.is("adj")) {
274 | QueryStringQueryBuilder queryBuilder = queryStringQuery(query);
275 |
276 | queryBuilder.analyzeWildcard(true);
277 |
278 | if(index.equals("")) {
279 | for(String field : schema.getTextualFieldNames()) {
280 | queryBuilder.field(field);
281 | }
282 | }
283 | else {
284 | queryBuilder.defaultField(index);
285 | }
286 |
287 | if(relation.is("all") || relation.is("adj")) {
288 | queryBuilder.defaultOperator(org.elasticsearch.index.query.Operator.AND);
289 | }
290 |
291 | return queryBuilder;
292 | }
293 |
294 | if(relation.is("==")) {
295 | return wildcardQuery(index, query);
296 | }
297 |
298 | return null;
299 | }
300 |
301 | private QueryBuilder regexpSearchClause(String index, Relation relation, SearchTerm searchTerm) {
302 |
303 | String query = searchTerm.getSearchTerm();
304 |
305 | if(relation.is("any") || relation.is("=") || relation.is("all")) {
306 | query = StringUtils.join(searchTerm.getTerms(), "");
307 | QueryStringQueryBuilder queryBuilder = queryStringQuery("/" + query + "/");
308 |
309 | queryBuilder.analyzeWildcard(true);
310 |
311 | if(index.equals("")) {
312 | for(String field : schema.getTextualFieldNames()) {
313 | queryBuilder.field(field);
314 | }
315 | }
316 | else {
317 | queryBuilder.defaultField(index);
318 | }
319 |
320 | if(relation.is("all")) {
321 | queryBuilder.defaultOperator(org.elasticsearch.index.query.Operator.AND);
322 | }
323 |
324 | return queryBuilder;
325 | }
326 |
327 | if(relation.is("==")) {
328 | if(relation.hasModifier("regexp")) {
329 | query = StringUtils.join(searchTerm.getTerms(), "");
330 | return regexpQuery(index, query);
331 | }
332 | else {
333 | return wildcardQuery(index, query);
334 | }
335 | }
336 |
337 | if(relation.is("adj")) {
338 | query = searchTerm.getRegexp(false);
339 | return regexpQuery("raw_" + index, query);
340 | }
341 |
342 | return null;
343 | }
344 |
345 | @Override
346 | public QueryBuilder visitSearchTerm(SearchTerm searchTerm) {
347 | Set fields = schema.getTextualFieldNames();
348 | String[] fieldNames = fields.toArray(new String[fields.size()]);
349 |
350 | MultiMatchQueryBuilder qb = multiMatchQuery(searchTerm.getSearchTerm(), fieldNames);
351 | qb.operator(org.elasticsearch.index.query.Operator.AND);
352 |
353 | return qb;
354 | }
355 |
356 | }
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/tree/visitor/EQL2JSTreeVisitor.java:
--------------------------------------------------------------------------------
1 | package org.iptc.extra.core.eql.tree.visitor;
2 |
3 | import org.iptc.extra.core.eql.tree.SyntaxTree;
4 | import org.iptc.extra.core.eql.tree.nodes.Clause;
5 | import org.iptc.extra.core.eql.tree.nodes.CommentClause;
6 | import org.iptc.extra.core.eql.tree.nodes.Index;
7 | import org.iptc.extra.core.eql.tree.nodes.Operator;
8 | import org.iptc.extra.core.eql.tree.nodes.PrefixClause;
9 | import org.iptc.extra.core.eql.tree.nodes.ReferenceClause;
10 | import org.iptc.extra.core.eql.tree.nodes.Relation;
11 | import org.iptc.extra.core.eql.tree.nodes.SearchClause;
12 | import org.iptc.extra.core.eql.tree.nodes.SearchTerm;
13 |
14 | /**
15 | * @author manosetro - Manos Schinas
16 | *
17 | * EXTRA2ESQueryVisitor performs a depth-first traversal of the syntax tree
18 | * and generates an HTML representation that can be used by the jsTree plugin of jQuery (https://www.jstree.com/).
19 | */
20 |
21 | public class EQL2JSTreeVisitor extends SyntaxTreeVisitor {
22 |
23 | @Override
24 | public String visitPrefixClause(PrefixClause prefixClause) {
25 | StringBuffer buffer = new StringBuffer();
26 |
27 | buffer.append("");
28 |
29 |
30 | buffer.append(visit(prefixClause.getOperator()));
31 | buffer.append("");
32 | for(Clause clause : prefixClause.getClauses()) {
33 | buffer.append(visit(clause));
34 | }
35 | buffer.append("
");
36 |
37 | buffer.append("");
38 |
39 | return buffer.toString();
40 | }
41 |
42 | @Override
43 | public String visitOperator(Operator operator) {
44 | StringBuffer buffer = new StringBuffer();
45 |
46 | buffer.append("");
47 | buffer.append(operator);
48 | buffer.append("");
49 |
50 | return buffer.toString();
51 | }
52 |
53 | @Override
54 | public String visitSearchClause(SearchClause searchClause) {
55 | StringBuffer buffer = new StringBuffer();
56 | buffer.append("");
57 |
58 | if(searchClause.hasIndex()) {
59 | buffer.append(visit(searchClause.getIndex()));
60 | buffer.append(visit(searchClause.getRelation()));
61 | }
62 | buffer.append(visit(searchClause.getSearchTerm()));
63 |
64 | buffer.append(" ");
65 | return buffer.toString();
66 | }
67 |
68 | @Override
69 | public String visitReferenceClause(ReferenceClause referenceClause) {
70 | StringBuffer buffer = new StringBuffer();
71 | buffer.append("");
72 |
73 | buffer.append("");
74 | buffer.append("@ref == ");
75 | buffer.append(referenceClause.getRuleId());
76 | buffer.append("");
77 |
78 | SyntaxTree syntaxTree = referenceClause.getRuleSyntaxTree();
79 | if(syntaxTree != null && syntaxTree.getRootNode() != null) {
80 | buffer.append("");
81 | buffer.append(visit(syntaxTree.getRootNode()));
82 | buffer.append("
");
83 | }
84 |
85 | buffer.append(" ");
86 | return buffer.toString();
87 | }
88 |
89 |
90 | @Override
91 | public String visitCommentClause(CommentClause commentClause) {
92 | return "";
93 | }
94 |
95 | @Override
96 | public String visitIndex(Index index) {
97 | StringBuffer buffer = new StringBuffer();
98 |
99 | buffer.append(" ");
100 | buffer.append(index.getName());
101 | buffer.append("");
102 |
103 | return buffer.toString();
104 | }
105 |
106 | @Override
107 | public String visitRelation(Relation relation) {
108 | StringBuffer buffer = new StringBuffer();
109 |
110 | buffer.append(" ");
111 | buffer.append(relation);
112 | buffer.append("");
113 |
114 | return buffer.toString();
115 | }
116 |
117 | @Override
118 | public String visitSearchTerm(SearchTerm searchTerm) {
119 | StringBuffer buffer = new StringBuffer();
120 |
121 | buffer.append(" ");
122 | buffer.append(searchTerm);
123 | buffer.append("");
124 |
125 | return buffer.toString();
126 | }
127 | }
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/tree/visitor/EQLPretifierVisitor.java:
--------------------------------------------------------------------------------
1 | package org.iptc.extra.core.eql.tree.visitor;
2 |
3 | import org.apache.commons.lang3.StringUtils;
4 | import org.iptc.extra.core.eql.tree.nodes.Clause;
5 | import org.iptc.extra.core.eql.tree.nodes.CommentClause;
6 | import org.iptc.extra.core.eql.tree.nodes.Operator;
7 | import org.iptc.extra.core.eql.tree.nodes.PrefixClause;
8 | import org.iptc.extra.core.eql.tree.nodes.ReferenceClause;
9 | import org.iptc.extra.core.eql.tree.nodes.SearchClause;
10 |
11 | /**
12 | * @author manosetro - Manos Schinas
13 | *
14 | * The class extends SyntaxTreeVisitor class to create a pretified version of the rule, expressed as a sytnax tree.
15 | * Namely, this visitor visits each node of the syntax tree in a depth first fashion,
16 | * concatenates the string representation of each node into a single string,
17 | * while adds newlines and tab characters to pretify it.
18 | *
19 | */
20 | public class EQLPretifierVisitor extends SyntaxTreeVisitor {
21 |
22 | private String newline;
23 | private String tab;
24 |
25 | public EQLPretifierVisitor(String newline, String tab) {
26 | this.newline = newline;
27 | this.tab = tab;
28 | }
29 |
30 | @Override
31 | public String visitPrefixClause(PrefixClause prefixClause) {
32 | StringBuffer buffer = new StringBuffer();
33 |
34 | buffer.append(StringUtils.repeat(tab, prefixClause.getDepth()));
35 | buffer.append("(");
36 |
37 | Operator operator = prefixClause.getOperator();
38 | buffer.append(operator.toString());
39 | buffer.append(newline);
40 |
41 | for(Clause clause : prefixClause.getClauses()) {
42 | buffer.append(visit(clause));
43 | }
44 |
45 | buffer.append(StringUtils.repeat(tab, prefixClause.getDepth()));
46 | buffer.append(")");
47 | buffer.append(newline);
48 |
49 | return buffer.toString();
50 | }
51 |
52 | @Override
53 | public String visitSearchClause(SearchClause searchClause) {
54 | StringBuffer buffer = new StringBuffer();
55 | buffer.append(StringUtils.repeat(tab, searchClause.getDepth()));
56 | buffer.append("(");
57 | if(searchClause.hasIndex()) {
58 | buffer.append(searchClause.getIndex());
59 | buffer.append(" ");
60 | buffer.append(searchClause.getRelation());
61 | buffer.append(" ");
62 | }
63 | buffer.append(searchClause.getSearchTerm());
64 | buffer.append(")");
65 | buffer.append(newline);
66 |
67 | return buffer.toString();
68 | }
69 |
70 | @Override
71 | public String visitCommentClause(CommentClause commentClause) {
72 | StringBuffer buffer = new StringBuffer();
73 | buffer.append(StringUtils.repeat(tab, commentClause.getDepth()));
74 | buffer.append(commentClause.getComment());
75 | buffer.append(newline);
76 | return buffer.toString();
77 | }
78 |
79 | @Override
80 | public String visitReferenceClause(ReferenceClause referenceClause) {
81 | StringBuffer buffer = new StringBuffer();
82 | buffer.append(StringUtils.repeat(tab, referenceClause.getDepth()));
83 | buffer.append("(@ref == " + referenceClause.getRuleId() + ")");
84 | buffer.append(newline);
85 | return buffer.toString();
86 | }
87 |
88 | }
89 |
90 |
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/tree/visitor/EQLValidator.java:
--------------------------------------------------------------------------------
1 | package org.iptc.extra.core.eql.tree.visitor;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 | import java.util.Set;
6 |
7 | import org.iptc.extra.core.eql.tree.extra.EQLOperator;
8 | import org.iptc.extra.core.eql.tree.nodes.Clause;
9 | import org.iptc.extra.core.eql.tree.nodes.CommentClause;
10 | import org.iptc.extra.core.eql.tree.nodes.ErrorMessageNode;
11 | import org.iptc.extra.core.eql.tree.nodes.Index;
12 | import org.iptc.extra.core.eql.tree.nodes.Node;
13 | import org.iptc.extra.core.eql.tree.nodes.Operator;
14 | import org.iptc.extra.core.eql.tree.nodes.PrefixClause;
15 | import org.iptc.extra.core.eql.tree.nodes.Relation;
16 | import org.iptc.extra.core.eql.tree.nodes.SearchClause;
17 | import org.iptc.extra.core.eql.tree.nodes.SearchTerm;
18 | import org.iptc.extra.core.eql.tree.utils.TreeUtils;
19 | import org.iptc.extra.core.types.Schema;
20 | import org.iptc.extra.core.types.Schema.Field;
21 |
22 | public class EQLValidator extends SyntaxTreeVisitor> {
23 |
24 | private Schema schema;
25 |
26 | public EQLValidator(Schema schema) {
27 | this.schema = schema;
28 | }
29 |
30 | public static List validate(Node root, Schema schema) {
31 | EQLValidator validator = new EQLValidator(schema);
32 |
33 | List invalidNodes = validator.visit(root);
34 | return invalidNodes;
35 | }
36 |
37 | @Override
38 | public List visitPrefixClause(PrefixClause prefixClause) {
39 |
40 | List invalidNodes = new ArrayList();
41 |
42 | Operator operator = prefixClause.getOperator();
43 | EQLOperator extraOperator = prefixClause.getEQLOperator();
44 |
45 | if(extraOperator == EQLOperator.SENTENCE || extraOperator == EQLOperator.NOT_IN_SENTENCE ||
46 | extraOperator == EQLOperator.PARAGRAPH || extraOperator == EQLOperator.NOT_IN_PARAGRAPH ||
47 | extraOperator == EQLOperator.DISTANCE || extraOperator == EQLOperator.NOT_WITHIN_DISTANCE ||
48 | extraOperator == EQLOperator.ORDER || extraOperator == EQLOperator.ORDER_AND_DISTANCE || extraOperator == EQLOperator.NOT_IN_PHRASE) {
49 |
50 | if(prefixClause.getClauses().size() != 2) {
51 | if(prefixClause.getClauses().size() == 1 && (extraOperator == EQLOperator.SENTENCE || extraOperator == EQLOperator.NOT_IN_SENTENCE ||
52 | extraOperator == EQLOperator.PARAGRAPH || extraOperator == EQLOperator.NOT_IN_PARAGRAPH)) {
53 | Clause childClause = prefixClause.getClause(0);
54 |
55 | if(!(childClause instanceof PrefixClause) || !EQLOperator.isWordDistanceOperator(((PrefixClause) childClause).getEQLOperator())) {
56 | ErrorMessageNode node = new ErrorMessageNode();
57 | node.setErrorMessage(operator.toString() + " (" + extraOperator + ") has invalid sub-statement. Only distance operators are permitted in single statements.");
58 |
59 | invalidNodes.add(node);
60 | operator.setValid(false);
61 | }
62 | }
63 | else {
64 | ErrorMessageNode node = new ErrorMessageNode();
65 | node.setErrorMessage(operator.toString() + " (" + extraOperator + ") has invalid number of statement. Only 2 statements are permitted.");
66 |
67 | invalidNodes.add(node);
68 | operator.setValid(false);
69 | }
70 | }
71 |
72 | int stemmedClauses = 0;
73 | Set searchClauses = TreeUtils.getSearchClauses(prefixClause);
74 | for(SearchClause sc : searchClauses) {
75 | if(sc.getRelation() != null && sc.getRelation().hasModifier("stemming")) {
76 | stemmedClauses++;
77 | }
78 | }
79 |
80 | if(stemmedClauses > 0 && searchClauses.size() != stemmedClauses) {
81 | ErrorMessageNode node = new ErrorMessageNode();
82 | node.setErrorMessage(operator.toString() + " (" + extraOperator + ") children mixes stemming and non-stemming.");
83 |
84 | invalidNodes.add(node);
85 | operator.setValid(false);
86 | }
87 |
88 | Set indices = TreeUtils.getIndices(prefixClause);
89 | Set searchTermClauses = TreeUtils.getSearchTermClauses((prefixClause));
90 |
91 | if(indices.size() > 1) {
92 | ErrorMessageNode node = new ErrorMessageNode();
93 | node.setErrorMessage(operator.toString() + " (" + extraOperator
94 | + ") has invalid number of indices: " + indices
95 | + ". Only 1 or no index is permitted.");
96 |
97 | invalidNodes.add(node);
98 | operator.setValid(false);
99 | }
100 | else if(!indices.isEmpty() && !searchTermClauses.isEmpty()) {
101 | ErrorMessageNode node = new ErrorMessageNode();
102 | node.setErrorMessage(operator.toString() + " (" + extraOperator + ") cannot mix index and non-index statements");
103 |
104 | invalidNodes.add(node);
105 | operator.setValid(false);
106 | }
107 | else {
108 | if(schema != null) {
109 | for(String index : indices) {
110 |
111 | Field field = schema.getField(index);
112 | if(field == null && index.contains("text_content")) {
113 | continue;
114 | }
115 |
116 | if(!field.hasSentences && (extraOperator == EQLOperator.SENTENCE || extraOperator == EQLOperator.NOT_IN_SENTENCE)) {
117 | ErrorMessageNode node = new ErrorMessageNode();
118 | node.setErrorMessage(operator.toString() + " (" + extraOperator + ") cannot be applied on a field (" + index + ") without sentences");
119 |
120 | invalidNodes.add(node);
121 | operator.setValid(false);
122 | }
123 |
124 | if(!field.hasParagraphs && (extraOperator == EQLOperator.PARAGRAPH || extraOperator == EQLOperator.NOT_IN_PARAGRAPH)) {
125 | ErrorMessageNode node = new ErrorMessageNode();
126 | node.setErrorMessage(operator.toString() + " (" + extraOperator + ") cannot be applied on a field (" + index + ") without paragraphs");
127 |
128 | invalidNodes.add(node);
129 | operator.setValid(false);
130 | }
131 | }
132 | }
133 | }
134 | }
135 |
136 | if(extraOperator == EQLOperator.MAXIMUM_OCCURRENCE || extraOperator == EQLOperator.MINIMUM_OCCURRENCE) {
137 | int clauses = 0;
138 | for(Clause clause : prefixClause.getClauses()) {
139 | if(!(clause instanceof CommentClause)) {
140 | clauses++;
141 | }
142 | }
143 |
144 | if(clauses != prefixClause.getSearchClause().size()) {
145 | ErrorMessageNode node = new ErrorMessageNode();
146 | node.setErrorMessage(operator.toString() + " can be applied only to search clauses.");
147 |
148 | invalidNodes.add(node);
149 | operator.setValid(false);
150 | }
151 | }
152 |
153 | if(extraOperator == null) {
154 | ErrorMessageNode node = new ErrorMessageNode();
155 | node.setErrorMessage(operator.toString() + " is not a valid EXTRA operator");
156 |
157 | invalidNodes.add(node);
158 | operator.setValid(false);
159 | }
160 |
161 | invalidNodes.addAll(visitChildren(prefixClause));
162 |
163 | return invalidNodes;
164 |
165 | }
166 |
167 | public List visitSearchClause(SearchClause searchClause) {
168 |
169 | List invalidRelations = new ArrayList();
170 |
171 | Index index = searchClause.getIndex();
172 | Relation relation = searchClause.getRelation();
173 | if(relation == null || index == null) {
174 | return invalidRelations;
175 | }
176 |
177 | if(relation != null && !relation.isValid()) {
178 | ErrorMessageNode node = new ErrorMessageNode();
179 | node.setErrorMessage(relation.toString() + " is not a valid EXTRA relation");
180 |
181 | invalidRelations.add(node);
182 | }
183 |
184 | SearchTerm searchTerm = searchClause.getSearchTerm();
185 | if(searchTerm.isRegexp()) {
186 |
187 | if(relation.hasModifier("stemming")) {
188 | ErrorMessageNode node = new ErrorMessageNode();
189 | node.setErrorMessage(relation.toString() + ". Stemming cannot be mixed with regex: " + searchTerm);
190 |
191 | invalidRelations.add(node);
192 | relation.setValid(false);
193 | }
194 |
195 | if(relation.is(">") || relation.is(">=") || relation.is("<") || relation.is("<=") || relation.is("within") || relation.is(">")) {
196 | ErrorMessageNode node = new ErrorMessageNode();
197 | node.setErrorMessage(relation.getRelation() + " relation cannot be mixed with regex: " + searchTerm);
198 |
199 | invalidRelations.add(node);
200 | relation.setValid(false);
201 | }
202 |
203 | }
204 |
205 | if(relation.hasModifier("regexp") && !searchTerm.isRegexp()) {
206 | ErrorMessageNode node = new ErrorMessageNode();
207 | node.setErrorMessage(relation + " has regexp modifier but no regexp has been detected in search term: " + searchTerm);
208 |
209 | invalidRelations.add(node);
210 | relation.setValid(false);
211 | }
212 |
213 | if(relation.hasModifier("masked") && !searchTerm.hasWildCards()) {
214 | ErrorMessageNode node = new ErrorMessageNode();
215 | node.setErrorMessage(relation + " has masked modifier but no wildcards have been detected in search term: " + searchTerm);
216 |
217 | invalidRelations.add(node);
218 | relation.setValid(false);
219 | }
220 |
221 | if(relation.hasModifier("stemming") && (relation.is(">") || relation.is(">=") || relation.is("<") || relation.is("<=") || relation.is("within") || relation.is(">"))) {
222 | ErrorMessageNode node = new ErrorMessageNode();
223 | node.setErrorMessage(relation.getRelation() + " relation cannot has stemming modifier.");
224 |
225 | invalidRelations.add(node);
226 | relation.setValid(false);
227 | }
228 | else if(schema != null && relation.hasModifier("stemming")) {
229 | Field field = schema.getField(index.getName());
230 | if(field != null && !field.textual) {
231 | ErrorMessageNode node = new ErrorMessageNode();
232 | node.setErrorMessage("Stemming modifier cannot be applied on a non-textual index: " + index.getName());
233 |
234 | invalidRelations.add(node);
235 | relation.setValid(false);
236 | }
237 | }
238 |
239 | return invalidRelations;
240 | }
241 |
242 | protected List aggregateResult(List aggregate, List nextResult) {
243 | aggregate.addAll(nextResult);
244 | return aggregate;
245 | }
246 |
247 | protected List defaultResult() {
248 | return new ArrayList();
249 | }
250 |
251 | }
252 |
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/tree/visitor/ReferenceClausesVisitor.java:
--------------------------------------------------------------------------------
1 | package org.iptc.extra.core.eql.tree.visitor;
2 |
3 | import java.util.ArrayList;
4 | import java.util.HashSet;
5 | import java.util.List;
6 | import java.util.Set;
7 |
8 | import org.iptc.extra.core.daos.RulesDAO;
9 | import org.iptc.extra.core.eql.EQLParser;
10 | import org.iptc.extra.core.eql.tree.SyntaxTree;
11 | import org.iptc.extra.core.eql.tree.nodes.ErrorMessageNode;
12 | import org.iptc.extra.core.eql.tree.nodes.ReferenceClause;
13 | import org.iptc.extra.core.types.Rule;
14 |
15 | public class ReferenceClausesVisitor extends SyntaxTreeVisitor> {
16 |
17 | private RulesDAO dao;
18 |
19 | private Set ruleIds = new HashSet();
20 |
21 | public ReferenceClausesVisitor(RulesDAO dao, String rootRuleId) {
22 | this.dao = dao;
23 | ruleIds.add(rootRuleId);
24 | }
25 |
26 | @Override
27 | public List visitReferenceClause(ReferenceClause referenceClause) {
28 |
29 | List errors = new ArrayList();
30 |
31 | String ruleId = referenceClause.getRuleId();
32 | if(ruleIds.contains(ruleId)) {
33 |
34 | referenceClause.setValid(false);
35 |
36 | ErrorMessageNode errorNode = new ErrorMessageNode();
37 | errorNode.setErrorMessage("Cyclic reference: " + ruleIds + " - " + ruleId);
38 |
39 | errors.add(errorNode);
40 | }
41 | else {
42 | ruleIds.add(ruleId);
43 | Rule rule = dao.get(ruleId);
44 | if(rule != null) {
45 | referenceClause.setRule(rule);
46 |
47 | String referencedEql = rule.getQuery();
48 | SyntaxTree referencedSyntaxTree = EQLParser.parse(referencedEql);
49 | referenceClause.setRuleSyntaxTree(referencedSyntaxTree);
50 |
51 | if(!referencedSyntaxTree.hasErrors() && referencedSyntaxTree.getRootNode() != null) {
52 | visit(referencedSyntaxTree.getRootNode());
53 | }
54 | else {
55 | referenceClause.setValid(false);
56 |
57 | ErrorMessageNode errorNode = new ErrorMessageNode();
58 | errorNode.setErrorMessage("Referenced rule " + ruleId + " has invalid syntax.");
59 |
60 | errors.add(errorNode);
61 | }
62 | }
63 | else {
64 | referenceClause.setValid(false);
65 |
66 | ErrorMessageNode errorNode = new ErrorMessageNode();
67 | errorNode.setErrorMessage("Referenced rule " + ruleId + " does not exist.");
68 |
69 | errors.add(errorNode);
70 | }
71 | }
72 |
73 | return errors;
74 | }
75 |
76 | protected List aggregateResult(List aggregate, List nextResult) {
77 | aggregate.addAll(nextResult);
78 | return aggregate;
79 | }
80 |
81 | protected List defaultResult() {
82 | return new ArrayList();
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/java/org/iptc/extra/core/eql/tree/visitor/SyntaxTreeVisitor.java:
--------------------------------------------------------------------------------
1 | package org.iptc.extra.core.eql.tree.visitor;
2 |
3 | import org.iptc.extra.core.eql.tree.nodes.Clause;
4 | import org.iptc.extra.core.eql.tree.nodes.CommentClause;
5 | import org.iptc.extra.core.eql.tree.nodes.Index;
6 | import org.iptc.extra.core.eql.tree.nodes.Node;
7 | import org.iptc.extra.core.eql.tree.nodes.Operator;
8 | import org.iptc.extra.core.eql.tree.nodes.PrefixClause;
9 | import org.iptc.extra.core.eql.tree.nodes.ReferenceClause;
10 | import org.iptc.extra.core.eql.tree.nodes.Relation;
11 | import org.iptc.extra.core.eql.tree.nodes.SearchClause;
12 | import org.iptc.extra.core.eql.tree.nodes.SearchTerm;
13 |
14 | /**
15 | * @author manosetro - Manos Schinas
16 | *
17 | * This class traverses the given syntax tree in a depth first fashion without performing any action.
18 | * That class should be extended by every visitor class.
19 | *
20 | */
21 | public class SyntaxTreeVisitor {
22 |
23 | public T visit(Node node) {
24 |
25 | if (node instanceof Index) {
26 | return visitIndex((Index) node);
27 | }
28 |
29 | if (node instanceof Relation) {
30 | return visitRelation((Relation) node);
31 | }
32 |
33 | if (node instanceof Operator) {
34 | return visitOperator((Operator) node);
35 | }
36 |
37 | if (node instanceof SearchTerm) {
38 | return visitSearchTerm((SearchTerm) node);
39 | }
40 |
41 | if (node instanceof CommentClause) {
42 | return visitCommentClause((CommentClause) node);
43 | }
44 |
45 | if (node instanceof SearchClause) {
46 | return visitSearchClause((SearchClause) node);
47 | }
48 |
49 | if (node instanceof ReferenceClause) {
50 | return visitReferenceClause((ReferenceClause) node);
51 | }
52 |
53 | if (node instanceof PrefixClause) {
54 | return visitPrefixClause((PrefixClause) node);
55 | }
56 |
57 | return null;
58 | }
59 |
60 | public T visitChildren(Node node) {
61 | T result = defaultResult();
62 | if(node.hasChildren()) {
63 | int n = node.getChildCount();
64 | for (int i=0; i findDocuments(String indexName, int page, int nPerPage) throws IOException {
123 | return findDocuments(null, indexName, page, nPerPage, null, null);
124 | }
125 |
126 | public ElasticSearchResponse findDocuments(QueryBuilder qb, String indexName, int page, int nPerPage) throws IOException {
127 | return findDocuments(qb, indexName, page, nPerPage, null, null);
128 | }
129 |
130 | public ElasticSearchResponse findDocuments(QueryBuilder qb, String indexName, int page, int nPerPage, Schema schema) throws IOException {
131 | return findDocuments(qb, indexName, page, nPerPage, schema, null);
132 | }
133 |
134 | public ElasticSearchResponse findDocuments(QueryBuilder qb, String indexName, int page, int nPerPage, QueryBuilder highlightQuery) throws IOException {
135 | return findDocuments(qb, indexName, page, nPerPage, null, highlightQuery);
136 | }
137 |
138 | /**
139 | *
140 | * @param qb The query expressed in Elastic search DSL
141 | * @param indexName The name of the index in elastic search
142 | * @param page Page number
143 | * @param nPerPage Number of documents per page
144 | * @param schema The schema of the documents in the index
145 | * @param highlightQuery The query used to highlight the results
146 | *
147 | * @return ElasticSearchResponse A set of documents that match the query
148 | *
149 | */
150 | public ElasticSearchResponse findDocuments(QueryBuilder qb, String indexName, int page, int nPerPage, Schema schema, QueryBuilder highlightQuery) throws IOException {
151 |
152 | Integer from = (page - 1) * nPerPage;
153 | Integer size = nPerPage;
154 |
155 | if(qb == null) {
156 | qb = matchAllQuery();
157 | }
158 |
159 | SearchRequestBuilder request = client.prepareSearch(indexName)
160 | .setTypes("documents")
161 | .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
162 | .setQuery(qb)
163 | .setFrom(from)
164 | .setSize(size)
165 | .setExplain(false);
166 |
167 | if(schema != null) {
168 | HighlightBuilder hlBuilder = new HighlightBuilder();
169 | for(String field : schema.getTextualFieldNames()) {
170 | hlBuilder.field(field).fragmentSize(0).numOfFragments(0);
171 | hlBuilder.field("stemmed_" + field).fragmentSize(0).numOfFragments(0);
172 | hlBuilder.field("case_sensitive_" + field).fragmentSize(0).numOfFragments(0);
173 | hlBuilder.field("literal_" + field).fragmentSize(0).numOfFragments(0);
174 | }
175 |
176 | hlBuilder.preTags("");
177 | hlBuilder.postTags("");
178 | if(highlightQuery != null) {
179 | hlBuilder.highlightQuery(highlightQuery);
180 | }
181 |
182 | request.highlighter(hlBuilder);
183 | }
184 |
185 | SearchResponse response = request.get();
186 |
187 | List documents = new ArrayList();
188 | SearchHits hits = response.getHits();
189 | float maxScore = hits.getMaxScore() > 0 ? hits.getMaxScore() : 1;
190 | for(SearchHit hit : hits) {
191 | float hitScore = hit.getScore();
192 | String source = hit.sourceAsString();
193 | Map