I wish to access outside variables for a test function that I am writing, in Groovy.
However, it doesn't seem that I can.
My code is like this:
Map<String, String> originalTableRowState = new HashMap<String, String>(),
newTableRowState
// if there is table data to get, and do actions on
def WebDriver driver = DriverFactory.getWebDriver()
def List<WebElement> dataRows = driver.findElements(
By.cssSelector('div.tab-pane.active .dataTables_scrollBody tbody tr:not(.dataTables_empty)'))
'if there\'s table data, this test should run'
if (dataRows.size() > 0) {
WebUI.comment('populate the tableRowState with the data from the first table row')
fetchFirstRowDataInto(originalTableRowState)
}
void fetchFirstRowDataInto(Map<String, String> tableRowState) {
List<WebElement> tableHeadings = driver.findElements(
By.cssSelector('div.tab-pane.active .dataTables_scrollHead th'))
WebElement firstRow = dataRows.get(0)
List<WebElement> dataCells = firstRow.findElements(
By.xpath('//td[not(#class="dataTables_empty") and not(*)]'))
for (int i = 0; i < dataCells.size(); i++) {
// save data to originalTableRowState with the table header text as the key
tableRowState.put(tableHeadings.get(i), dataCells.get(i))
}
}
and when I run it, it greets me with the error saying that Variable 'driver' is not defined outside test case. I just added the def keywords to the driver,dataRows definintions.
How to make driver,dataRows accessible inside functions, without passing them in as parameters?
I fixed the method variable-access issue by declaring it a JS-like closure:
/* change void fetchFirstRowDataInto(Map<String, String> tableRowState) { to def fetchFirstRowDataInto = { Map<String, String> tableRowState -> */
and putting the definition above the invocation.
I welcome any better solutions...
Related
I am trying to read an excel sheet and save it in Java Data Structure List> list
Basically I am having a table in the excel sheet
|Name|FirstName|emp_ID|Age|
|aaaa|bbbbbbbbb|111111|40 |
|cccc|fffffffff|222222|25 |
where the keys will be
|Name|FirstName|emp_ID|Age|
my list of map should look as bellow
{Name=aaaa, FirstName=bbbbbbbbb, emp_ID=111111, Age=40}
{Name=cccc, FirstName=fffffffff, emp_ID=222222, Age=25}
but my list is storing the second map two times
{Name=cccc, FirstName=fffffffff, emp_ID=222222, Age=25}
{Name=cccc, FirstName=fffffffff, emp_ID=222222, Age=25}
any idea how to fix the issue please or any better suggestion
Thank You in advance
Here is the code That I wrote
workbook = WorkbookFactory.create(inStream);
workSheet = workbook.getSheetAt(0);
DataFormatter df = new DataFormatter();
Map<String, String> myMap = new LinkedHashMap<>();
List<Map<String, String>> list = new ArrayList<>();
row = workSheet.getRow(0);
ArrayList<String> headersName = new ArrayList<String>();
for (int j = 0; j <= row.getPhysicalNumberOfCells(); j++) {
row.getCell(j);
if ((df.formatCellValue(row.getCell(j)).isEmpty())) {
continue;
} else {
headersName.add(df.formatCellValue(row.getCell(j)));
}
}
System.out.println(headersName);
OUTER: for (Row myrow : workSheet) {
for (int i = 0; i < myrow.getLastCellNum(); i++) {
if (myrow.getRowNum() == 0) {
continue OUTER;
}
String value = df.formatCellValue(myrow.getCell(i));
myMap.put(headersName.get(i), value);
}
list.add(myMap);
}
System.out.println(list.size());
for (Map<String, String> map : list) {
System.out.println(map);
}
the print of my list
{Name=cccc, FirstName=fffffffff, emp_ID=222222, Age=25}
{Name=cccc, FirstName=fffffffff, emp_ID=222222, Age=25}
The issue lies in myMap.put(headersName.get(i), value). In that line, if the key already exists in the map, it overrides it. But later on, list.add(myMap) passes a reference to the same map, not a new version of it. So on the second iteration, when the line overrides the value, it overrides it on the first map already in the list. See below for a basic example of the same issue.
import java.util.*;
public class Example {
public static void main(String []args) {
Map<String, String> myMap = new LinkedHashMap<>();
List<Map<String, String>> list = new ArrayList<>();
myMap.put("abc", "123");
list.add(myMap);
System.out.println(list.get(0));
myMap.put("abc", "234");
System.out.println(myMap);
System.out.println(list.get(0));
}
}
To fix it, look into deep copies so that changing one doesn't change them all.
I'm using java to modify some groovy code using reflection.
The original groovy code is of the form:
void method() {
A(processId);
B();
}
I need to modify this to inject processId:
void callMethod() {
int processId = rand();
invokeMethod(
{->
A(processId);
B();
}, processId);
}
void invokeMethod(Closure closure, int processId) {
doSomething(processId);
closure.call();
}
Note: invokeMethod() is an existing method and is not injected.
When I modified the original code as above, I'm getting this error:
"groovy.lang.MissingPropertyException: No such property: processId"
I've tried setting the variableScope of the "callMethod" method to include the "processId" variable as a DeclaredVariable.
For reference, here is the code I used to do this:
private void wrapMethod(#NonNull MethodNode node)
{
VariableExpression var = new VariableExpression("processId", new ClassNode(Integer.class));
var.setClosureSharedVariable(true);
//Generate a statement that creates a processId
Statement statement = GeneralUtils.declS(var, GeneralUtils.callThisX("getUUID"));
ClosureExpression methodClosure = createMethodClosure(node);
ArgumentListExpression args = createArgumentListExpression(methodClosure,
varX("processId"));
MethodCallExpression method = GeneralUtils.callThisX("invokeMethod", args);
BlockStatement newMethodCode = GeneralUtils.block(statement, GeneralUtils.stmt(method));
newMethodCode.setVariableScope(methodClosure.getVariableScope().copy());
//Just to be safe, modifying the scope of the node as well.
VariableScope newScope = node.getVariableScope().copy();
newScope.putDeclaredVariable(var);
node.setCode(newMethodCode);
node.setVariableScope(newScope);
}
private ClosureExpression createMethodClosure(MethodNode node) {
//Get code from within node to dump into a closure.
BlockStatement block = (BlockStatement) node.getCode();
//Setting closureScope and adding processId
VariableScope closureScope = block.getVariableScope().copy();
closureScope.getReferencedLocalVariablesIterator()
.forEachRemaining(x -> x.setClosureSharedVariable(true));
Variable var = new VariableExpression("processId", new ClassNode(Integer.class));
var.setClosureSharedVariable(true);
closureScope.putDeclaredVariable(var);
ClosureExpression methodClosure = GeneralUtils.closureX(block);
methodClosure.setVariableScope(closureScope);
return methodClosure;
}
I've verified the code to check that 'processId' is accessible within the invokeMethod() block and that the resultant code from the injection is as expected.
Any ideas on why this is not working?
Here is my code
public static void save(IgniteContext igniteContext, String cacheName, Dataset<Row> dataSet) {
CacheConfiguration<BinaryObject, BinaryObject> cacheConfiguration = new CacheConfiguration<BinaryObject, BinaryObject>(cacheName)
.setAtomicityMode(CacheAtomicityMode.ATOMIC)
.setBackups(0)
.setAffinity(new RendezvousAffinityFunction(false, 2))
.setIndexedTypes(BinaryObject.class, BinaryObject.class);
IgniteCache<BinaryObject, BinaryObject> rddCache = igniteContext.ignite()
.getOrCreateCache(cacheConfiguration)
.withKeepBinary();
rddCache.clear();
IgniteRDD<BinaryObject, BinaryObject> igniteRDD = igniteContext.fromCache(cacheName);
StructField[] fields = dataSet.schema().fields();
RDD<BinaryObject> binaryObjectJavaRDD = dataSet.toJavaRDD().map(row -> {
BinaryObjectBuilder valueBuilder = igniteContext.ignite().binary().builder(BinaryObject.class.getCanonicalName());
for (int i = 0; i < fields.length; i++) {
valueBuilder.setField(fields[i].name(), convertValue(String.valueOf(row.get(i)), fields[i].dataType())); //convertValue converts value to specific datatype
}
return valueBuilder.build();
}).rdd();
igniteRDD.saveValues(binaryObjectJavaRDD);
}
I have a problem with the above code, that is even after successful completion of this method cache remains empty. Dataset has 20 rows so that is not the problem.
The other problem is that if I use savePairs method from IgniteRDD then I have to generate the Key by myself(here Key is BinaryObject), so how to do that?
update
saveDFInPairs(IgniteContext igniteContext, Dataset<Row> dataSet, IgniteRDD<BinaryObject, BinaryObject> igniteRDD) {
StructField[] fields = dataSet.schema().fields();
JavaRDD<Tuple2<BinaryObject, BinaryObject>> rdd = dataSet.toJavaRDD().map(row -> {
BinaryObjectBuilder keyBuilder = igniteContext.ignite()
.binary().builder("TypeName");
keyBuilder.setField("id", row.mkString().hashCode());
BinaryObject key = keyBuilder.build();
BinaryObjectBuilder valueBuilder = igniteContext.ignite()
.binary().builder("TypeName");
for (int i = 0; i < fields.length; i++) {
valueBuilder.setField(fields[i].name(), convert(row, i, fields[i].dataType()));
}
BinaryObject value = valueBuilder.build();
return new Tuple2<>(key, value);
});
igniteRDD.savePairs(rdd.rdd(), true);
}
Couple of considerations:
The type name (the one passed to the builder() method) should be a meaningful name representing the data type. Do not use BinaryObject class name for this.
setIndexedTypes(BinaryObject.class, BinaryObject.class) is incorrect. This should specify classes to be processed for query annotations. If you don't have classes, you can use QueryEntity to configure queries. See this page for further details: https://apacheignite.readme.io/docs/sql-queries
Other than that code looks correct. I would recommend to try with default settings and check if it works this way. Also it's not very clear how you check that the data is in cache or not.
I'm trying to do something rather simple. I would like to wrap the whole method code into an additional closure block that would measure the execution time. Right now I'm getting a really not helpful error message:
Error:Groovyc: NPE while processing Test.groovy
Annotation:
#Retention(RetentionPolicy.SOURCE)
#Target([ElementType.METHOD])
#GroovyASTTransformationClass(["WithTimingASTTransformation"])
public #interface WithTiming {
}
My wrapping closure:
class Benchmark {
static def measureTime(Closure cl) {
def start = System.currentTimeMillis()
def result = cl()
def time = System.currentTimeMillis() - start
println "it took $time"
result
}
}
My Transformation:
#GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
class WithTimingASTTransformation implements ASTTransformation {
#Override
void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {
MethodNode method = astNodes[1]
method.code = wrap(method)
}
private Statement wrap(MethodNode method) {
def newBlock = new BlockStatement()
newBlock.addStatement(
new ExpressionStatement(
new StaticMethodCallExpression(
new ClassNode(Benchmark),
'measureTime',
new ArgumentListExpression(
new ClosureExpression(new Parameter[0], method.code)
))))
newBlock
}
}
I'm really stuck here and don't know how can I debug the problem.
There is an answer on a similar topic (wrapping whole method body into a try/catch block here). This works fine but my case is slightly different.
In my case similar NPE was coming from:
java.lang.NullPointerException
at org.codehaus.groovy.classgen.asm.ClosureWriter.createClosureClass(ClosureWriter.java:194)
at org.codehaus.groovy.classgen.asm.ClosureWriter.getOrAddClosureClass(ClosureWriter.java:159)
at org.codehaus.groovy.classgen.asm.ClosureWriter.writeClosure(ClosureWriter.java:90)
at org.codehaus.groovy.classgen.AsmClassGenerator.visitClosureExpression(AsmClassGenerator.java:673)
Whereas:
if (parameters == null || expression.getVariableScope() == null) {
parameters = Parameter.EMPTY_ARRAY;
} else if (parameters.length == 0) {
// let's create a default 'it' parameter
Parameter it = new Parameter(ClassHelper.OBJECT_TYPE, "it", ConstantExpression.NULL);
parameters = new Parameter[]{it};
Variable ref = expression.getVariableScope().getDeclaredVariable("it");
if (ref != null) it.setClosureSharedVariable(ref.isClosureSharedVariable());
}
and line 194 (as of https://github.com/groovy/groovy-core/commit/a52d0d3c5dd1cbb342992d36235171718a563c8b) is:
Variable ref = expression.getVariableScope().getDeclaredVariable("it");
Thus you need to define a VariableScope for your ClosureExpression. I had to add tracing into org.codehaus.groovy.ast.ClosureWriter to find this, because there is an issue with exception display on stage of Class Generation - both in IntelliJ Idea and in Groovy Console - it does not show proper lines of code.
Furthermore, I think that either ClosureWriter or ClosureExpression constructor can be fixed to work aligned by default - without this NPE. I will possibly submit an issue to Groovy Jira for this.
Now I am able to inject closure expression in my code. But struggling to call this closure.
Getting:
groovy.lang.MissingMethodException: No signature of method: com.a9ae0b01f0ffc.VSMSGEN.implementation.T_visa_recon_generator$_convert_vts_log_to_ctf_closure2.call() is applicable for argument types: () values: []
Long story short, after some iterations my method-wrapping AST looks like this:
BlockStatement bs = new BlockStatement()
ClosureExpression closureExp = new ClosureExpression( methodNode.parameters, methodNode.code )
closureExp.variableScope = new VariableScope() // <- this does the trick!
bs.addStatement new ExpressionStatement( new StaticMethodCallExpression( new ClassNode( TransactionUtil ), 'wrap', new ArgumentListExpression( closureExp ) ) )
methodNode.code = bs
The line closureExp.variableScope = new VariableScope() avoids the NPE in ClosureWriter.java:194 and the whole thing runs like a charm!
Hope it helps someone...
I am building an asp.net site in .net framework 4.0, and I am stuck at the method that supposed to call a .cs class and get the query result back here is my method call and method
1: method call form aspx.cs page:
helper cls = new helper();
var query = cls.GetQuery(GroupID,emailCap);
2: Method in helper class:
public IQueryable<VariablesForIQueryble> GetQuery(int incomingGroupID, int incomingEmailCap)
{
var ctx = new some connection_Connection();
ObjectSet<Members1> members = ctx.Members11;
ObjectSet<groupMember> groupMembers = ctx.groupMembers;
var query = from m in members
join gm in groupMembers on m.MemberID equals gm.MemID
where (gm.groupID == incomingGroupID) && (m.EmailCap == incomingEmailCap)
select new VariablesForIQueryble(m.MemberID, m.MemberFirst, m.MemberLast, m.MemberEmail, m.ValidEmail, m.EmailCap);
//select new {m.MemberID, m.MemberFirst, m.MemberLast, m.MemberEmail, m.ValidEmail, m.EmailCap};
return query ;
}
I tried the above code with IEnumerable too without any luck. This is the code for class VariablesForIQueryble:
3:Class it self for taking anonymouse type and cast it to proper types:
public class VariablesForIQueryble
{
private int _emailCap;
public int EmailCap
{
get { return _emailCap; }
set { _emailCap = value; }
}`....................................
4: and a constructor:
public VariablesForIQueryble(int memberID, string memberFirst, string memberLast, string memberEmail, int? validEmail, int? emailCap)
{
this.EmailCap = (int) emailCap;
.........................
}
I can't seem to get the query result back, first it told me anonymous type problem, I made a class after reading this: link text; and now it tells me constructors with parameters not supported. Now I am an intermediate developer, is there an easy solution to this or do I have to take my query back to the .aspx.cs page.
If you want to project to a specific type .NET type like this you will need to force the query to actually happen using either .AsEnumerable() or .ToList() and then use .Select() against linq to objects.
You could leave your original anonymous type in to specify what you want back from the database, then call .ToList() on it and then .Select(...) to reproject.
You can also clean up your code somewhat by using an Entity Association between Groups and Members using a FK association in the database. Then the query becomes a much simpler:
var result = ctx.Members11.Include("Group").Where(m => m.Group.groupID == incomingGroupID && m.EmailCap == incomingEmailCap);
You still have the issue of having to do a select to specify which columns to return and then calling .ToList() to force execution before reprojecting to your new type.
Another alternative is to create a view in your database and import that as an Entity into the Entity Designer.
Used reflection to solve the problem:
A: Query, not using custom made "VariablesForIQueryble" class any more:
//Method in helper class
public IEnumerable GetQuery(int incomingGroupID, int incomingEmailCap)
{
var ctx = new some_Connection();
ObjectSet<Members1> members = ctx.Members11;
ObjectSet<groupMember> groupMembers = ctx.groupMembers;
var query = from m in members
join gm in groupMembers on m.MemberID equals gm.MemID
where ((gm.groupID == incomingGroupID) && (m.EmailCap == incomingEmailCap)) //select m;
select new { m.MemberID, m.MemberFirst, m.MemberLast, m.MemberEmail, m.ValidEmail, m.EmailCap };
//select new VariablesForIQueryble (m.MemberID, m.MemberFirst, m.MemberLast, m.MemberEmail, m.ValidEmail, m.EmailCap);
//List<object> lst = new List<object>();
//foreach (var i in query)
//{
// lst.Add(i.MemberEmail);
//}
//return lst;
//return query.Select(x => new{x.MemberEmail,x.MemberID,x.ValidEmail,x.MemberFirst,x.MemberLast}).ToList();
return query;
}
B:Code to catch objects and conversion of those objects using reflection
helper cls = new helper();
var query = cls.GetQuery(GroupID,emailCap);
if (query != null)
{
foreach (var objRow in query)
{
System.Type type = objRow.GetType();
int memberId = (int)type.GetProperty("MemberID").GetValue(objRow, null);
string memberEmail = (string)type.GetProperty("MemberEmail").GetValue(objRow, null);
}
else
{
something else....
}