Print the specific field from list - groovy

Please help to print the assetNumber. Need to search for specific assetname and print its associated assetNumber using list in groovy.
Currently, it does not print value. It struck once search criteria is entered.
class Main
{
public static void main(String[] args)
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in))
List list = new ArrayList()
Asset asset = new Asset()
def name
def assetNumber
def assigneeName
def assignedDate
def assetType
String userInput = "Yes"
while(userInput.equalsIgnoreCase("Yes"))
{
println "Enter the asset details:"
asset.name = br.readLine()
asset.assetNumber= Integer.parseInt(br.readLine())
asset.assigneeName = br.readLine()
asset.assignedDate = Date.parse('dd/MM/yyyy', br.readLine())
list.add(asset)
println "Do you want to continue(yes/no)"
userInput = br.readLine()
}
println "Enter the asset type:"
assetType = br.readLine()
println "Asserts with type "+assetType+":"
def items = list.findAll{p->p.name == assetType }
items.each { println it.assetNumber }
}
}
class Asset
{
def name
def assetNumber
def assigneeName
def assignedDate
}

You are overwriting the value in your Asset object, then adding the same object to the list every time
If you create a new asset inside the loop (rather than outside it), it should work...
Something like this:
import groovy.transform.*
class Main {
static main(args) {
def console = System.console()
def list = []
while (true) {
def name = console.readLine 'Enter the asset name:'
def number = console.readLine 'Enter the asset number:'
def assignee = console.readLine 'Assignee name:'
def date = console.readLine 'Date (dd/MM/yyyy):'
list << new Asset(
name: name,
assetNumber: Integer.parseInt(number),
assigneeName: assignee,
assignedDate: Date.parse('dd/MM/yyyy', date)
)
if ('Y' != console.readLine('Enter another (y/n):')?.take(1)?.toUpperCase()) {
break
}
}
def type = console.readLine 'Enter the asset type:'
println "Assets with type $type:"
list.findAll { p -> p.name == type }
.each { println it.assetNumber }
}
#Canonical
static class Asset {
def name
def assetNumber
def assigneeName
def assignedDate
}
}

Related

Finding a String from list in a String is not efficient enough

def errorList = readFile WORKSPACE + "/list.txt"
def knownErrorListbyLine = errorList.readLines()
def build_log = new URL (Build_Log_URL).getText()
def found_errors = null
for(knownError in knownErrorListbyLine) {
if (build_log.contains(knownError)) {
found_errors = build_log.readLines().findAll{ it.contains(knownError) }
for(error in found_errors) {
println "FOUND ERROR: " + error
}
}
}
I wrote this code to find listed errors in a string, but it takes about 20 seconds.
How can I improve the performance? I would love to learn from this.
Thanks a lot!
list.txt contains a string per line:
Step ... was FAILED
[ERROR] Pod-domainrouter call failed
#type":"ErrorExtender
[postDeploymentSteps] ... does not exist.
etc...
And build logs is where I need to find these errors.
Try this:
def errorList = readFile WORKSPACE + "/list.txt"
def knownErrorListbyLine = errorList.readLines()
def build_log = new URL (Build_Log_URL)
def found_errors = null
for(knownError in knownErrorListbyLine) {
build_log.eachLine{
if ( it.contains(knownError) ) {
println "FOUND ERROR: " + error
}
}
}
This might be even more performant:
def errorList = readFile WORKSPACE + "/list.txt"
def knownErrorListbyLine = errorList.readLines()
def build_log = new URL (Build_Log_URL)
def found_errors = null
build_log.eachLine{
for(knownError in knownErrorListbyLine) {
if ( it.contains(knownError) ) {
println "FOUND ERROR: " + error
}
}
}
Attempt using the last one relying on string eachLine instead.
def errorList = readFile WORKSPACE + "/list.txt"
def knownErrorListbyLine = errorList.readLines()
def build_log = new URL (Build_Log_URL).getText()
def found_errors = null
build_log.eachLine{
for(knownError in knownErrorListbyLine) {
if ( it.contains(knownError) ) {
println "FOUND ERROR: " + error
}
}
}
Try to move build_log.readLines() to the variable outside of the loop.
def errorList = readFile WORKSPACE + "/list.txt"
def knownErrorListbyLine = errorList.readLines()
def build_log = new URL (Build_Log_URL).getText()
def found_errors = null
def buildLogByLine = build_log.readLines()
for(knownError in knownErrorListbyLine) {
if (build_log.contains(knownError)) {
found_errors = buildLogByLine.findAll{ it.contains(knownError) }
for(error in found_errors) {
println "FOUND ERROR: " + error
}
}
}
Update: using multiple threads
Note: this may help in case errorList size is large enough. And also if the matching errors distributed evenly.
def sublists = knownErrorListbyLine.collate(x)
// int x - the sublist size,
// depends on the knownErrorListbyLine size, set the value to get e. g. 4 sublists (threads).
// Also do not use more than 2 threads per CPU. Start from 1 thread per CPU.
def logsWithErrors = []// list for store results per thread
def lock = new Object()
def threads = sublists.collect { errorSublist ->
Thread.start {
def logs = build_log.readLines()
errorSublist.findAll { build_log.contains(it) }.each { error ->
def results = logs.findAll { it.contains(error) }
synchronized(lock) {
logsWithErrors << results
}
}
}
}
threads*.join() // wait for all threads to finish
logsWithErrors.flatten().each {
println "FOUND ERROR: $it"
}
Also, as was suggested earlier by other user, try to measure the logs download time, it could be the bottleneck:
def errorList = readFile WORKSPACE + "/list.txt"
def knownErrorListbyLine = errorList.readLines()
def start = Calendar.getInstance().timeInMillis
def build_log = new URL(Build_Log_URL).getText()
def end = Calendar.getInstance().timeInMillis
println "Logs download time: ${(end-start)/1000} ms"
def found_errors = null

Unable to retrieve data from an xml to place in an array

I am trying to retrieve a number of flightids from an xml and place them in an array but I keep getting no data displayed. It doesn't seem to find 'flights' but I am not sure why, is there anything wrong with the code below?
import groovy.xml.XmlUtil
def groovyUtils = new com.eviware.soapui.support.GroovyUtils(context)
def response = context.expand( '${SOAP Request#Response}' )
def parsedxml = new XmlSlurper().parseText(response)
def tests= parsedxml.'**'.findAll { it.name() == 'b:TestId'}
log.info tests
//Get the rate plan codes
def testId = { option ->
def res = option.'**'.findAll {it.name() == 'b:TestId'}
if (res) return option.TestId.text()
null
}
def testIdArray = []
tests.each { if (testId(it)) testIdArray << testId(it) }
for (int i = 0; i < testIdArray.size(); i++) {
log.error "TestIds: " + testIdArray[i]
}
log.warn testIdArray.size()
Below is the xml:
<s:Envelope xxx="xxx" xxx="xxx">
<s:Header>
<a:Action s:mustUnderstand="1">xxx</a:Action>
</s:Header>
<s:Body>
<XML1 xmlns="xxx">
<XML2 xmlns:b="xxx" xmlns:i="xxx">
<XML3>
<b:TestId>000000</b:TestId>
</XML3>
<XML3>
<b:TestId>000000</b:TestId>
</XML3>
</XML2>
</XML1>
</s:Body>
</s:Envelope>
Pass the xml string response to below response variable
def xml = new XmlSlurper().parseText(response)
def getFlightIds = { type = '' ->
type ? xml.'**'.findAll { it.name() == type }.collect { it.FlightId.text() } : xml.'**'.findAll {it.FlightId.text()}
}
//Get the respective flight ids
//Use the desired one as you have mentioned none
def inFlightIds = getFlightIds('InboundFlightInformation')
def outFlightIds = getFlightIds('OutboundFlightInformation')
def allFlightIds = getFlightIds()
log.info "Inbound flight ids: ${inFlightIds}"
log.info "Outbound flight ids: ${outFlightIds}"
log.info "All flight ids: ${allFlightIds}"
You can quickly try online Demo
it.name() in your case it is a NodeChild
so, it.name() returns just a name without prefix. it means you should compare it to FlightId (without b: )
if you want to check a namespace (linked to a prefix) then your lookup must be like this:
def flights = parsedxml.'**'.findAll { it.name() == 'FlightId' && it.namespaceURI()=='xxx' }

Groovy map constructor keys to different variable names

I have JSON looking like:
{
"days": [
{
"mintemp": "21.8"
}
]
}
With Groovy, I parse it like this:
class WeatherRow {
String mintemp
}
def file = new File("data.json")
def slurper = new JsonSlurper().parse(file)
def days = slurper.days
def firstRow = days[0] as WeatherRow
println firstRow.mintemp
But actually, I would like to name my instance variable something like minTemp (or even something completely random, like numberOfPonies). Is there a way in Groovy to map a member of a map passed to a constructor to something else?
To clarify, I was looking for something along the lines of #XmlElement(name="mintemp"), but could not easily find it:
class WeatherRow {
#Element(name="mintemp")
String minTemp
}
Create a constructor that takes a map.
Runnable example:
import groovy.json.JsonSlurper
def testJsonStr = '''
{"days": [
{ "mintemp": "21.8" }
]}'''
class WeatherRow {
String minTemp
WeatherRow(map) {
println "Got called with constructor that takes a map: $map"
minTemp = map.mintemp
}
}
def slurper = new JsonSlurper().parseText(testJsonStr)
def days = slurper.days
def firstRow = days[0] as WeatherRow
println firstRow.minTemp
Result:
Got called with constructor that takes a map: [mintemp:21.8]
21.8
(of course you'd remove the println line, it's just there for the demo)
You can achieve this using annotation and simple custom annotation processor like this:
1. Create a Custom Annotation Class
#Retention(RetentionPolicy.RUNTIME)
#interface JsonDeserializer {
String[] names() default []
}
2. Annotate your instance fields with the custom annotation
class WeatherRow{
#JsonDeserializer(names = ["mintemp"])
String mintemp;
#JsonDeserializer(names = ["mintemp"])
String minTemp;
#JsonDeserializer(names = ["mintemp"])
String numberOfPonies;
}
3. Add custom json deserializer method using annotation processing:
static WeatherRow fromJson(def jsonObject){
WeatherRow weatherRow = new WeatherRow();
try{
weatherRow = new WeatherRow(jsonObject);
}catch(MissingPropertyException ex){
//swallow missing property exception.
}
WeatherRow.class.getDeclaredFields().each{
def jsonDeserializer = it.getDeclaredAnnotations()?.find{it.annotationType() == JsonDeserializer}
def fieldNames = [];
fieldNames << it.name;
if(jsonDeserializer){
fieldNames.addAll(jsonDeserializer.names());
fieldNames.each{i ->
if(jsonObject."$i")//TODO: if field type is not String type custom parsing here.
weatherRow."${it.name}" = jsonObject."$i";
}
}
};
return weatherRow;
}
Example:
def testJsonStr = '''
{
"days": [
{
"mintemp": "21.8"
}
]
}'''
def parsedWeatherRows = new JsonSlurper().parseText(testJsonStr);
assert WeatherRow.fromJson(parsedWeatherRows.days[0]).mintemp == "21.8"
assert WeatherRow.fromJson(parsedWeatherRows.days[0]).minTemp == "21.8"
assert WeatherRow.fromJson(parsedWeatherRows.days[0]).numberOfPonies == "21.8"
Check the full working code at groovyConsole.

Convert NodeBuilder result to object hierarchy

Imagine nodeBuilder used to express an object hierarchy:
class TestBuilder {
static main(args) {
def builder = new NodeBuilder()
def ulcDate = new Date(107,0,1)
def invoices = builder.invoices{
invoice(date: ulcDate){
item(count:5){
product(name:'ULC', dollar:1499)
}
item(count:1){
product(name:'Visual Editor', dollar:499)
}
}
invoice(date: new Date(106,1,2)){
item(count:4) {
product(name:'Visual Editor', dollar:499)
}
}
}
}
class Invoice {
List items
Date date
}
class LineItem {
Product product
int count
int total()
{
return product.dollar * count
}
}
class Product {
String name
def dollar
}
How do I actually convert invoices object, which was generated by NodeBuilder, to an instance of Invoice class with everything configured from the invoices object? I probably have to have use GPath to do so (?) but how would that code look like?
The reason that I need to do so is that the other methods of other classes require an instance of the Invoice class to operate further and won't accept a NodeBuilder output I guess.
I think the easiest way is simply to do node traversal for your particular set of objects.
Example:
import groovy.util.*
////////////
// build Node tree as asked in original post
def builder = new NodeBuilder()
def ulcDate = new Date(107,0,1)
def invoices = builder.invoices {
invoice(date: ulcDate) {
item(count:5) {
product(name:'ULC', dollar:1499)
}
item(count:1) {
product(name:'Visual Editor', dollar:499)
}
}
invoice(date: new Date(106,1,2)){
item(count:4) {
product(name:'Visual Editor', dollar:499)
}
}
}
////////////
// define objects. It is easy to have these in Java
class Invoice {
def date
def items = []
}
class Item {
def count
def product
}
class Product {
def name
def dollar
}
////////////
// convert from nodes to objects
def invoiceNodeList = invoices.get("invoice")
def invoiceList = []
invoiceNodeList.each { def invoiceNode ->
def date = invoiceNode.attribute("date")
Invoice invoice = new Invoice(date: date)
invoiceNode.children().each { def itemNode ->
def count = itemNode.attribute("count")
Product product = null
// assume only one Product per Item, but we'll
// use children() for simplicity
itemNode.children().each { def productNode ->
def name = productNode.attribute("name")
def dollar = productNode.attribute("dollar")
product = new Product(name: name, dollar: dollar)
}
Item item = new Item(count: count, product: product)
invoice.items << item
}
invoiceList << invoice
}
////////////
// print out objects
invoiceList.each { Invoice invoice ->
println "--------"
println invoice.date
invoice.items.each { Item item ->
println item.count
println item.product.name
println item.product.dollar
}
}
Giving a slight tweak to your base classes:
class Invoice {
List lineItems = []
Date date
String toString() {
String ret = "Invoice $date $lineItems"
}
}
class LineItem {
Product product
int count
int total() {
product.dollar * count
}
String toString() {
"$product * $count"
}
}
class Product {
String name
int dollar
String toString() {
"$name ($dollar)"
}
}
Means you can easily use ObjectGraphBuilder to build your list:
List invoices = new ObjectGraphBuilder(classLoader: getClass().classLoader).with {
[
invoice( date: new Date( 107, 0, 1 ) ) {
lineItem( count: 5 ) {
product( name: 'ULC', dollar: 1499 )
}
lineItem(count:1){
product(name:'Visual Editor', dollar:499)
}
},
invoice(date: new Date(106,1,2)){
lineItem(count:4) {
product(name:'Visual Editor', dollar:499)
}
}
]
}
If it isn't possible to tweak the base classes, you can customise how properties are looked up by setting the resolvers before building your graph

Dynamic object graph navigation in Groovy

folks!
I want to be able to navigate Groovy object graph dynamically, having the path in string:
def person = new Person("john", new Address("main", new Zipcode("10001", "1234")))
def path = 'address.zip.basic'
I know that I can access a property in map notation, but it's only one level deep:
def path = 'address'
assert person[path] == address
Is there any way to evaluate deeper path?
Thanks!
This can be achieved by overriding the getAt operator and traversing the property graph. The following code uses Groovy Category but inheritance or mixins could also be used.
class ZipCode {
String basic
String segment
ZipCode(basic, segment) {
this.basic = basic
this.segment = segment
}
}
class Address {
String name
ZipCode zip
Address(String name, ZipCode zip) {
this.name = name
this.zip = zip
}
}
class Person {
String name
Address address
Person(String name, Address address) {
this.name = name
this.address = address
}
}
#Category(Object)
class PropertyPath {
static String SEPARATOR = '.'
def getAt(String path) {
if (!path.contains(SEPARATOR)) {
return this."${path}"
}
def firstPropName = path[0..path.indexOf(SEPARATOR) - 1]
def remainingPath = path[path.indexOf(SEPARATOR) + 1 .. -1]
def firstProperty = this."${firstPropName}"
firstProperty[remainingPath]
}
}
def person = new Person('john', new Address('main', new ZipCode('10001', '1234')))
use(PropertyPath) {
assert person['name'] == 'john'
assert person['address.name'] == 'main'
assert person['address.zip.basic'] == '10001'
}
PropertyPath.SEPARATOR = '/'
use(PropertyPath) {
assert person['address/zip/basic'] == '10001'
}

Resources