Drools rule that fires when 2 objects share an attribute - java

So I'm just beginning to tinker with Drools and am enjoying it,tough the documentation(the bits I've found at least) is a bit deep end all over.
I'm trying to create a rule that will fire when two objects share an attribute but can't seem to get the condition right. If I'm reading the documentation right, this should work:
(Yes, I am using Magic:The Gathering rules as a base for playing around because I know them well)
rule "704.5j. If two or more planeswalkers that share a planeswalker type are on the battlefield, all are put into their owners' graveyards. This is called the 'planeswalker uniqueness rule'."
when
$c1 : Card (CurrentZone == ZoneType.Battlefield , Types.contains("Planeswalker") , $subtype : Types.get(1) , $c1ID : ID );
$c2 : Card (CurrentZone == ZoneType.Battlefield , Types.contains("Planeswalker") , Types.contains($subtype) , ID != $c1ID);
then
System.out.println("PW Uniqueness: " + $c1.getName() + " | " + $c2.getName());
$c1.setCurrentZone(ZoneType.Graveyard);
$c2.setCurrentZone(ZoneType.Graveyard);
end
Will I have to do this on the java side of things?
EDIT: Also, tutorial/guide suggestions for Drools are extremely welcome.

If you havent got this working, maybe moving a section to an IF in the Then section as to avoid declaring stuff in when and then recalling it in the same section,
rule "704.5j. If two or more planeswalkers that share a planeswalker type are on the battlefield, all are put into their owners' graveyards. This is called the 'planeswalker uniqueness rule'."
when
$c1 : Card (CurrentZone == ZoneType.Battlefield , Types.contains("Planeswalker") ,($subtype : Types.get(1)));
$c2 : Card (CurrentZone == ZoneType.Battlefield , Types.contains("Planeswalker") ,($subtype2 : Types.get(1)) );
then
if (($subtype == $subtype2) && ($c1.getID() == $c2.getID()))
{
System.out.println("PW Uniqueness: " + $c1.getName() + " | " + $c2.getName());
$c1.setCurrentZone(ZoneType.Graveyard);
$c2.setCurrentZone(ZoneType.Graveyard);
}
end
This is a little long winded. but i usually avoid declaring and checking against the same variables in the When, i declare everything i need (using arrays and evals most the time) and then check with IF statments within the Then.
Also as # Marko said, get the first attribute down, check its working and just add one check at a time. sometimes i build big rules using stupid amounts of IFs etc. once its working. i start to bring the size down. its easier to reduce a working things, than repair a small broken thing :)

Related

Clang-format with 8-space rule for complex if statements in Java

I'm trying to set-up clang-format so that it confirms to Oracle's Java Code Conventions. The document provides numerous examples, of which the most important are:
function(longExpression1, longExpression2, longExpression3,
longExpression4, longExpression5);
var = function1(longExpression1,
function2(longExpression2,
longExpression3));
longName1 = longName2 * (longName3 + longName4 - longName5)
+ 4 * longname6;
if ((condition1 && condition2)
|| (condition3 && condition4)
||!(condition5 && condition6)) {
doSomethingAboutIt();
}
The following options cover the first three cases:
AlignAfterOpenBracket: Align
AlignAfterOpenBracket: Align
BreakBeforeBinaryOperators: true
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: true
ColumnLimit: 80
IndentWidth: 4
TabWidth: 8
About the fourth case, the document says: "Line wrapping for if statements should generally use the 8-space rule, since conventional (4 space) indentation makes seeing the body difficult." I went through all options, but I don't think I came across something.
Its admittedly a rather fastidious issue, but I thought maybe there is something I overlooked. Any ideas would be greatly appreciated. Thanks in advance!

Tesseract 4.5 return multiple results for same image structure

Hello everyone i have problem with tess4j with Arabic.trainddata
the problem is when i get result two times the results were different
like this
the first output :
"| رقم القيد ? : 139\n" +
"18/02/2020 : ?التاريخ\n" +
"SYRIA H.O : ?الفرع?\n" +
the second output :
"رقم القيد ? : 439\n" +
"التاريخ :08/07/2020\n" +
"الفرع : ?SYRIA H.O?\n" +
the last raw is reverse and it could be for other raw in another output
please i need solution for make ocr always start read from RTL or to give me always the same result
and thank for all :)
Tesseract learns or adapts its results over successive runs. You'll need to clear its adaptive classifier or cache (via ClearAdaptiveClassifier, ClearPersistentCache​, or Clear method) to get the same result across subsequent runs.

GATE write annotation ID as a feature

I was wondering is someone can help me out here. I think this could be of use for anyone trying to conduct machine learning on GATE (General Architecture for Text Engineering). So basically to conduct machine learning I first need to add some code to a few jape files so my output XML file would print out the Annotation Id value as a feature. An example is provided below:
<Annotation Id="1491" Type="Person" StartNode="288" EndNode="301">
<Feature>
<Name className="java.lang.String">id</Name>
<Value className="java.lang.String">1491</Value>
</Feature>
(Note that the feature value of 1491 matches the Annotation Id="1491". This is what I want.)
WHY I NEED THIS:
I am conducting machine learning on a plain text document that initially contains no annotation. I am using the June 2012 training course that is on the GATE website as a guide while doing this. I am specifically following the Module 11: Relations tutorial (it finds employment relationships between person and organization). I utilize the corpus of 93 pre-annotated documents for training and then apply that learned module on my document. But first I run my document through ANNIE. It creates many annotations and features but not everything that I need for machine learning. I've learned through trial/error and investigation that my annotated document must contain features with the Annotation Id for every "Person" and "Organization" type. I recognize that the configuration file (relations-config.xml) that is used in the Batch Learning PR looks for id features for "Person" and "Organization" types. It will not run if these ID features are not present. So I add this manually and then run it through the machine learning "APPLICATION" mode. It works rather nicely. However I clearly do not want to add the id features to my XML file manually every time.
WHAT I HAVE FIGURED OUT WITH THE GATE CODE:
I believe I have found the code files (final.jape, org_context.jape and name_context.jape) that I need to alter so they can add that id feature to every annotation that contains "Person" and "Organization". I don't understand the language that GATE uses very well (I'm a mechanical engineer, not a software engineer) and this is probably why I can't figure this out (Ha!). Anyhow, I could be off and may need to add a few more lines in for the jape file to work properly, but I feel like I've pinpointed it pretty closely. There are two sections of code that are similar but slightly different, which are currently the bane of my existence. The first one goes through an iterator loop, the second one does not. I copy/pasted those 2 those below with a line stating WHAT_DO_I_PUT_HERE that indicate where I think my problem and solution lies. I would be very grateful if someone can help me with what I need to write to get my result.
Thank you!
- Todd
//////////// First section of code ////////////////
Rule: PersonFinal
Priority: 30
//({JobTitle}
//)?
(
{TempPerson.kind == personName}
)
:person
-->
{
gate.FeatureMap features = Factory.newFeatureMap();
gate.AnnotationSet personSet = (gate.AnnotationSet)bindings.get("person");
gate.Annotation person1Ann = (gate.Annotation)personSet.iterator().next();
gate.AnnotationSet firstPerson = (gate.AnnotationSet)personSet.get("TempPerson");
if (firstPerson != null && firstPerson.size()>0)
{
gate.Annotation personAnn = (gate.Annotation)firstPerson.iterator().next();
if (personAnn.getFeatures().containsKey("gender")) features.put("gender", personAnn.getFeatures().get("gender"));
}
features.put("id", WHAT_DO_I_PUT_HERE.getId().toString());
features.put("rule1", person1Ann.getFeatures().get("rule"));
features.put("rule", "PersonFinal");
outputAS.add(personSet.firstNode(), personSet.lastNode(), "Person", features);
outputAS.removeAll(personSet);
}
//////////// Second section of code ////////////////
Rule:OrgContext1
Priority: 1
// company X
// company called X
(
{Token.string == "company"}
(({Token.string == "called"}|
{Token.string == "dubbed"}|
{Token.string == "named"}
)
)?
)
(
{Unknown.kind == PN}
)
:org
-->
{
gate.AnnotationSet org = (gate.AnnotationSet) bindings.get("org");
gate.FeatureMap features = Factory.newFeatureMap();
features.put("id", WHAT_DO_I_PUT_HERE.getId().toString());
features.put("rule ", "OrgContext1");
outputAS.add(org.firstNode(), org.lastNode(), "Organization", features);
outputAS.removeAll(org);
}
You cannot access the annotation id before the actual annotation is created. My solution of this problem:
Rule:PojemId
(
{PojemD}
):pojem
-->
{
AnnotationSet matchedAnns = bindings.get("pojem");
Annotation ann = matchedAnns.get("PojemD").iterator().next();
FeatureMap pojemFeatures = ann.getFeatures();
gate.FeatureMap features = Factory.newFeatureMap();
features.putAll(pojemFeatures);
features.put("annId", ann.getId());
inputAS.remove(ann);
Integer id = outputAS.add(matchedAnns.firstNode(), matchedAnns.lastNode(), "PojemD", features);
features.put("id", id);
}
It's quite simple. You have to mark the annotation on the Right Hand Side (RHS) of the rule by some label (token_match in my example bellow) and then, on the Left Hand Side (LHS) of the rule, just obtain corresponding AnnotationSet form bindings variable and iterate through annotations (usually there is only a single annotation in it) and copy corresponding IDs to the output.
Phase: Main
Input: Token
Rule: WriteTokenID
(
({Token}):token_match
)
-->
{
AnnotationSet as = bindings.get("token_match");
for (Annotation a : as)
{
FeatureMap features = Factory.newFeatureMap();
features.put("origTokenId", a.getId());
outputAS.add(a.getStartNode(), a.getEndNode(), "NewToken", features);
}
}
In your code, you probably want to mark {TempPerson.kind == personName}and {Unknown.kind == PN} somehow like bellow.
(
({TempPerson.kind == personName}):temp_person
)
:person
and
(
{Token.string == "company"}
(({Token.string == "called"}|
{Token.string == "dubbed"}|
{Token.string == "named"}
)
)?
)
(
({Unknown.kind == PN}):unknown_org
)
:org
And them use bindings.get("temp_person") and bindings.get("unknown_org") respectively.

How to merge two ASTs?

I'm trying to implement a tool for merging different versions of some source code. Given two versions of the same source code, the idea would be to parse them, generate the respective Abstract Source Trees (AST), and finally merge them into a single output source keeping grammatical consistency - the lexer and parser are those of question ANTLR: How to skip multiline comments.
I know there is class ParserRuleReturnScope that helps... but getStop() and getStart() always return null :-(
Here is a snippet that illustrates how I modified my perser to get rules printed:
parser grammar CodeTableParser;
options {
tokenVocab = CodeTableLexer;
backtrack = true;
output = AST;
}
#header {
package ch.bsource.ice.parsers;
}
#members {
private void log(ParserRuleReturnScope rule) {
System.out.println("Rule: " + rule.getClass().getName());
System.out.println(" getStart(): " + rule.getStart());
System.out.println(" getStop(): " + rule.getStop());
System.out.println(" getTree(): " + rule.getTree());
}
}
parse
: codeTabHeader codeTable endCodeTable eof { log(retval); }
;
codeTabHeader
: comment CodeTabHeader^ { log(retval); }
;
...
Assuming you have the ASTs (often difficult to get in the first place, parsing real languages is often harder than it looks), you first have to determine what they have in common, and build a mapping collecting that information. That's not as easy as it looks; do you count a block of code that has moved, but is the same exact subtree, as "common"? What about two subtrees that are the same except for consistent renaming of an identifier? What about changed comments? (most ASTs lose the comments; most programmers will think this is a really bad idea).
You can build a variation of the "Longest Common Substring" algorithm to compare trees. I've used that in tools that I have built.
Finally, after you've merged the trees, now you need to regenerate the text, ideally preserving most of the layout of the original code. (Programmers hate when you change the layout they so loving produced). So your ASTs need to capture position information, and your regeneration has to honor that where it can.
The call to log(retval) in your parser code looks like it's going to happen at the end of the rule, but it's not. You'll want to move the call into an #after block.
I changed log to spit out a message as well as the scope information and added calls to it to my own grammar like so:
script
#init {log("#init", retval);}
#after {log("#after", retval);}
: statement* EOF {log("after last rule reference", retval);}
-> ^(STMTS statement*)
;
Parsing test input produced the following output:
Logging from #init
getStart(): [#0,0:4='Print',<10>,1:0]
getStop(): null
getTree(): null
Logging from after last rule reference
getStart(): [#0,0:4='Print',<10>,1:0]
getStop(): null
getTree(): null
Logging from #after
getStart(): [#0,0:4='Print',<10>,1:0]
getStop(): [#4,15:15='<EOF>',<-1>,1:15]
getTree(): STMTS
The call in the after block has both the stop and tree fields populated.
I can't say whether this will help you with your merging tool, but I think this will at least get you past the problem with the half-populated scope object.

Java: Object loop stuck after first entry

So I have this little piece of code:
System.out.println("Size: " + mounts.keySet().size());
for (JHttpPath entry : mounts.keySet()) {
System.out.println("Got one: " + entry.getPath());
if (entry.getDomain().equalsIgnoreCase(path.getDomain()) && entry.getPath().equalsIgnoreCase(path.getPath())) {
System.out.println("WIN! " + entry.getPath());
return mounts.get(entry);
}
}
And this is the output:
Size: 4
Got one: /host
But it should be:
Size: 4
Got one: /host
Got one: /cookie
Got one: /
Got one: /
Any idea on what is blocking the statement :P
Well, as this is commercial software I can't provide one of the classes.
This may seem very like a beginner question but it is actually very odd.
The problem: The String where I used .equals(IgnoresCase) was defined as NULL, this resulted in an exception which is ignored on purpose by my software.
My bad, thanks for all input.
Solution code:
(entry.getDomain() == null || path.getDomain() == null || entry.getDomain().equalsIgnoreCase(path.getDomain()))
The obvious answer is that one of your methods executed after the Got one line is printed is entering into an endless loop. GetDomain() from the looks of it.
Maybe if we knew a bit more about JHttpPath ? I assume it's a custom class, as Google only turns up this very question when Googling JHttpPath.

Categories

Resources