Dynamic expression evaluator at runtime - java

I have an app that I wanted to customize using some sort of expression evaluator.
I wanted to change certain parts of the app without recompiling my code.
I am thinking of using groovy expression.
Say for example, I have a feature that can be enabled/disabled by supplying expression:
In this case, this feature will be enabled
Example 1:
EXPRESSION : status = part-time || status = hourly
INPUT: status = part-time
In this case, this feature will be disabled
Example 2:
EXPRESSION : status = part-time || status = hourly
INPUT: status = permanent
Users are required to enter an expression and an Input. Expression should evaluate to a boolean expression
They can also change the expression and the program will pick up this by some evaluation.
These expressions by the way are documented and is exposed by me to the user of my application
Example 3:
EXPRESSION : salary > 10000
INPUT: salary = 7000
I have seen a program before that does this and they say that they uses groovy under the hood.
But I cannot get my head wrap around the concept.
Can somebody please give me an idea?

Alternative approach using evaluate. Your variables are defined in binding, evaluate contains the expression:
// setup binding
def emp = [status:'permanent', name:'Hugo']
def binding = new Binding()
binding.status = 'permanent'
binding.status2 = 'part-time'
binding.salary = 7000
binding.employee = emp
println binding.variables
// evaluate the script
def ret = new GroovyShell(binding).evaluate("status == 'part-time' || status == 'hourly'")
println ret
ret = new GroovyShell(binding).evaluate("status2 == 'part-time' || status2 == 'hourly'")
println ret
ret = new GroovyShell(binding).evaluate("salary > 10000")
println ret
ret = new GroovyShell(binding).evaluate("employee.status == 'permanent' || employee.status == 'hourly'")
println ret
returns
[status:permanent, status2:part-time, salary:7000, employee:[status:permanent, name:Hugo]]
false
true
false
true

the usual call of groovy script:
import groovy.lang.Binding;
import groovy.lang.Script;
import groovy.lang.GroovyShell;
Binding binding = new Binding();
binding.setProperty("status", "part-time");
GroovyShell shell = new GroovyShell(binding);
Script script = shell.parse(" status == 'part-time' || status == 'hourly' ");
Object result = script.run();
after run the binding will be populated with new values of the variables.
you can cache the parsed script (or class of the parsed script) because parsing/compiling is expensive process.
as alternative - the easiest script evaluation:
Object result = groovy.util.Eval.me("status", "part-time",
" status == 'part-time' || status == 'hourly' ");

Related

How can I create a Locale with a specific script code?

I'm trying to convert this String az_AZ_#Latn, found here, to a Locale but I'm unable to parse the #Latn part.
If I do new Locale("az_AZ_#Latn") I lose the #Latn part (the Script code).
I've tried as well using the LocaleUtils from commons-lang but I get an error saying that it's an invalid format.
As written in the docs:
It is not possible to set a script code on a Locale object in a
release earlier than JDK 7.
But you can use the Locale builder to make it like this:
Locale locale = new Locale.Builder().setLanguage("az").setRegion("AZ").setScript("Latn").build();
You can get the Script it by calling locale.getScript()
Edit:
Here's a method I made for converting a string into a locale (doesn't work for extensions):
public static Locale stringToLocale(String locale){
if(locale == null || locale.isEmpty()) return null;
String[] parts = locale.split("_");
if(parts.length == 1) return new Locale(parts[0]);
if(parts.length == 2) return new Locale(parts[0],parts[1]);
if(parts.length == 3)
if(parts[2].charAt(0) != '#') return new Locale(parts[0],parts[1],parts[2]);
else return new Locale.Builder().setLanguage(parts[0]).setRegion(parts[1]).setScript(parts[2].substring(1)).build();
if(parts.length == 4) return new Locale.Builder().setLanguage(parts[0]).setRegion(parts[1]).setVariant(parts[2]).setScript(parts[3].charAt(0)=='#'? parts[3].substring(1):null).build();
return null;
}
//works for the toString output expect for extensions. test: for(Locale l: Locale.getAvailableLocales()) System.out.println(l.equals(stringToLocale(l.toString())));
// output : true true true...
usage:
Locale l = stringToLocale("az_AZ_#Latn");
Locale.Builder is able to handle script information for locales. The documentation of the Builder class also includes this example code:
Locale aLocale = new Locale.Builder().setLanguage("sr")
.setScript("Latn")
.setRegion("RS")
.build();
By using the builder you would have to do the splitting of the string yourself and also removal of any unsupported characters like #.
Using the 3-arg contructor java.util.Locale.Locale(String, String, String) is not correct since you probably don't intend to specify a variant using Latn but a script instead.
If the format is consistent (your input looks always the same) you can split by # and by _ and get the parts.
See the following example:
var input = "az_AZ#Latn"
var lns = input.split("#")
var l = lns[0].split("_")
var locale = new Locale.Builder()
.setLanguage(l[0])
.setRegion(l[1])
.setScript(lns[1])
.build()
locale.getLanguage() // ==> "az"
locale.getCountry() // ==> "AZ"
locale.getScript() //==> "Latn"
That #Latn refers to the script which in this case is Latin.
From java documentation:
script
ISO 15924 alpha-4 script code. You can find a full list of valid script codes in the IANA Language Subtag Registry (search for "Type: script"). The script field is case insensitive, but Locale always canonicalizes to title case (the first letter is upper case and the rest of the letters are lower case).
Well-formed script values have the form [a-zA-Z]{4}
Example: "Latn" (Latin), "Cyrl" (Cyrillic)
If you want to create a Locale using the script you can use its builder.
For instance:
Locale locale = new Locale.Builder()
.setLanguage("az")
.setRegion("AZ")
.setScript("Latn")
.build();

Using RapidMiner Textprocessing plugin in Java - Not able to use 'Document' object in the code

I am using RapidMiner 5. I want to make a text preprocessing module to use with a categorization system. I created a process in RapidMiner with these steps.
Tokenize
Transform Case
Stemming
Filtering stopwords
Generating n-grams
I want to write a script to do spell correction for these words. So, I used 'Execute Script' operator and wrote a groovy script for doing this (from here- raelcunha). This is the code ( helped by RapidMiner community) I wrote in execute Script operator of rapid miner.
Document doc=input[0]
List<Token> newTokens = new LinkedList<Token>();
nWords=train("set2.txt")
for (Token token : doc.getTokenSequence()) {
//String output=correct((String)token.getToken(),nWords)
println token.getToken();
Token nToken = new Token(correct("garbge",nWords), token);
newTokens.add(nToken);
}
doc.setTokenSequence(newTokens);
return doc;
This is the code for spell correction. ( Thanks to Norvig.)
import com.rapidminer.operator.text.Document;
import com.rapidminer.operator.text.Token;
import java.util.List;
import java.util.LinkedList;
def train(f){
def n = [:]
new File(f).eachLine{it.toLowerCase().eachMatch(/\w+/){n[it]=n[it]?n[it]+1:1}}
n
}
def edits(word) {
def result = [], n = word.length()-1
for(i in 0..n) result.add(word[0..<i] + word.substring(i+1))
for(i in 0..n-1) result.add(word[0..<i] + word[i+1] + word[i, i+1] + word.substring(i+2))
for(i in 0..n) for(c in 'a'..'z') result.add(word[0..<i] + c + word.substring(i+1))
for(i in 0..n) for(c in 'a'..'z') result.add(word[0..<i] + c + word.substring(i))
result
}
def correct(word, nWords) {
if(nWords[word]) return word
def list = edits(word), candidates = [:]
for(s in list) if(nWords[s]) candidates[nWords[s]] = s
if(candidates.size() > 0) return candidates[candidates.keySet().max()]
for(s in list) for(w in edits(s)) if(nWords[w]) candidates[nWords[w]] = w
return candidates.size() > 0 ? candidates[candidates.keySet().max()] : word
}
I am getting String index out of bounds exception while calling edits method.
And, I do not know how to debug this because rapidminer just tells me that there is an issue in the Execute Script operator and not saying which line of script caused this issue.
So, I am planning to do the same thing by creating an operator in Java as mentioned here-How to extend RapidMiner
The things I did:
Included all jar files from RapidMiner Lib folder , (C:\Program Files (x86)\Rapid-I\RapidMiner5\lib ) into the build path of my java project.
Started coding using the same guide the link to which is given above.
Input for my operator is a Document ( com.rapidminer.operator.text.Document)
as in the script.
But, I am not able to use this Document object in this code. Can you tell me why? Where are the text processing jars located?
For using the plugin jars, should we add some other locations to the BuildPath?

CliBuilder argument without dash

Using Groovy CliBuilder, ideally I would like to have an cmd-line as follows:
./MyProgram.groovy CommandName -arg1 -arg2 -arg3
Is is possible to parse pull out the CommandName as an argument using CliBuilder?
You can do that if you set the property stopAtNonOption to false so that the parsing does not stop in CommandName. Then you can get the command from CliBuilder options. A tiny example below:
def test(args) {
def cli = new CliBuilder(usage: 'testOptions.groovy [command] -r -u', stopAtNonOption: false)
cli.with {
r longOpt: 'reverse', 'Reverse command'
u longOpt: 'upper', 'Uppercase command'
}
def options = cli.parse(args)
def otherArguments = options.arguments()
def command = otherArguments ? otherArguments[0] : 'defaultCommand'
def result = command
if (options.r) {
result = result.reverse()
}
if (options.u) {
result = result.toUpperCase()
}
result
}
assert 'myCommand' == test(['myCommand'])
assert 'MYCOMMAND' == test(['myCommand', '-u'])
assert 'dnammoCym' == test(['myCommand', '-r'])
assert 'DNAMMOCYM' == test(['myCommand', '-r', '-u'])
assert 'defaultCommand' == test([])

Getting JSON from System.out.println

I am trying to do sentiment analysis and project the values on Google Visualization.
I am calling this python script using my java program
Code Snippet (for AlchemyAPI)
https://github.com/AlchemyAPI/alchemyapi-twitter-python
I wrote a java program to call the python script.
import java.io.*;
public class twitmain {
public String twittersentiment(String[] args) throws IOException {
// set up the command and parameter
String pythonScriptPath = "/twitter/analyze.py"; // I'm calling AlchemyAPI
String[] cmd = new String[2 + args.length];
cmd[0] = "C:\\Python27\\python.exe";
cmd[1] = pythonScriptPath;
for (int i = 0; i < args.length; i++) {
cmd[i + 2] = args[i];
}
// create runtime to execute external command
Runtime rt = Runtime.getRuntime();
Process pr = rt.exec(cmd);
// retrieve output from python script
BufferedReader bfr = new BufferedReader(new InputStreamReader(
pr.getInputStream()));
String line = ""; int i=0;
while ((line = bfr.readLine()) != null) {
System.out.println(line);
}
return line;
}
}
Output:
I am getting tweets and final statistics as follows:
##########################################################
# The Tweets #
##########################################################
#uDiZnoGouD
Date: Mon Apr 07 05:07:19 +0000 2014
To enjoy in case you win!
To help you sulk in case you loose!
#IndiavsSriLanka #T20final http://t.co/hRAsIa19zD
Document Sentiment: positive (Score: 0.261738)
##########################################################
# The Stats #
##########################################################
Document-Level Sentiment:
Positive: 3 (60.00%)
Negative: 1 (20.00%)
Neutral: 1 (20.00%)
Total: 5 (100.00%)
Problem (Question):
How do I just scrape Positive, Negitive, Neutral and send it to Google Visualization? (I.e make a JSON?)
Any help would be really appreciated.
Shoot, I just realized you were asking the other way around. To write the parsing app in Java.
Anyway the idea would be the same, but the language would differ.
But that would also mean that you have access to the sources of the python app, so you can maybe dig around there, and you could dump the result object into the console as a JSON object.
Original answer in python:
You should identify the types of lines and parse them and construct the JSON object yourself.
Like for every line:
import re
json_obj = {}
pattern = "^(\w+): (\d) \((\d{2}\.\d{2}%)\)$"
match = re.match(pattern, line)
if match:
prop_obj = { "value": match[2], "percent": match[3] }
json_obj[match[1]] = prop_obj
This would transform the line:
Positive: 3 (60.00%)
into
{
Positive: {
value: "3"
percent: "60.00%"
}
}
Taking this idea further, the parsing rules should be a dictionary of pattern-extractor_methods as key-values
var parse_rules = {
"^(\w+): (\d) \((\d{2}\.\d{2}%)\)$":
def (matches):
return { match[1]: { "value": match[2], "percent": match[3] }}
, ...
}
And for each line you would test against the parse rules and execute the methods if a match is found, and the result of the method is merged in the JSON result object
This is a lot of work (depending on the complexity of the java app, but I'd go this way if the Java app cannot be modified.
Regex explanation & example

PYTHON - Won't print to a .java document?

I have an issue with python.
Here is my code.
http://pastebin.com/yRu5WGKd
Whenever I select Item or Pickaxe, it prints just fine. Anything below that won't print.. Please help!?!
Note: I also used pastebin because I find it easiest to read.
Your actual mistake is the 62nd line, if ItemType == 3 & ItemStr == 1: - it should start with elif, or it breaks your (really nasty) if-cascade.
Another potential problem: in all your comparisons, ie if ItemType == 1 & ItemStr == 1:, you are using bitwise and (&) when you should be using logical and (and).
Here is a rewritten version. It is less than half the length, data-driven, and makes it much easier to spot inconsistencies (did you mean 'Diamond' or 'Emerald' in your material types?):
class Item(object):
types = [
('Item', 'Item'),
('Pickaxe', 'ItemPickaxe'),
('Shovel', 'ItemSpade'),
('Axe', 'ItemAxe'),
('Hoe', 'ItemHoe'),
('Sword', 'ItemSword')
]
strengths = [
('Diamond', 'EnumToolMaterial.EMERALD'), # ?? You might want to doublecheck this...
('Gold', 'EnumToolMaterial.GOLD'),
('Iron', 'EnumToolMaterial.IRON'),
('Stone', 'EnumToolMaterial.STONE'),
('Wood', 'EnumToolMaterial.WOOD'),
]
javastring = 'public static final {type} {name} = new {type}({id}, {strength}).setItemName("{name}");'
#classmethod
def prompt_for_item(cls):
s = "Please enter your item's name:\n"
name = raw_input(s).strip()
types = ["[{}] {}".format(i,n[0]) for i,n in enumerate(cls.types, 1)]
s = "Please enter item type:\n{}\n".format('\n'.join(types))
type_ = int(raw_input(s)) - 1
s = "Please enter item id (unique int):\n"
id = int(raw_input(s))
strengths = ["[{}] {}".format(i,n[0]) for i,n in enumerate(cls.strengths, 1)]
s = "Please enter item strength:\n{}\n".format('\n'.join(strengths))
strength = int(raw_input(s)) - 1
return cls(name, type_, id, strength)
def __init__(self, name, type_, id, strength):
self.name = name
self.type = type_
self.id = id
self.strength = strength
def write_to_file(self, fname=None):
if fname is None:
fname = '{}.java'.format(self.name)
with open(fname, 'w') as outf:
cls = type(self)
outf.write(
cls.javastring.format(
type = cls.types[self.type][1],
name = self.name,
id = self.id,
strength = cls.strengths[self.strength][1]
)
)
def main():
it = Item.prompt_for_item()
it.write_to_file()
print 'File has been written'
if __name__=="__main__":
main()

Categories

Resources