At work, we have to generate a report for our client that changes its parameters several times during the week.
This report is generated from a single table on our database.
For example, imagine a table that has 100 columns and I have to generate a report with only 5 columns today, but tomorrow I have to generate with 95 of them.
With this in mind, I created a TO class with all the columns of the specified table and my query returns all columns (SELECT * FROM TABLE).
What I'm trying to create is a dynamic form to generate the report.
I first thought on create a simple frame with a list of the columns listed as check boxes and the user would select the columns that he wants (of course with a button to Select All and another to Deselect All).
As all of the columns have the same name as the attributes of the TO class, I developed the following code (I have Google this):
Class c = Test.class;
for(int i = 0; i < listOfAttributes.length; i++)
{
auxText += String.valueOf( c.getMethod( "get" + listOfAttributes[i]).invoke( this, null ) );
}
Is this the better way to do what I need to?
Thanks in advance.
Obs.: the getters of the TO class have the pattern "getAttribute_Name".
Note: This question is different from the one where the user is asking HOW to invoke some method given a certain name. I know how to do that. What I'm asking is if this is the better way to solve the problem I described.
My Java is a little more limited, but I believe that's about as good as you're going to get using reflection.
Class<?> c = Test.class;
for (String attribute : listOfAttributes) {
auxText += String.valueOf(c.getMethod("get" + attribute).invoke(this, null));
}
But since this sounds like it's from potentially untrusted data, I would advise using a HashMap in this case, with each method explicitly referenced. First of all, it explicitly states what methods can be dynamically called. Second, it's more type safe, and compile-time errors are way better than runtime errors. Third, it is likely faster, since it avoids reflection altogether. Something to the effect of this:
private static final HashMap<String, Supplier<Object>> methods = new HashMap<>();
// Initialize all the methods.
static {
methods.set("Foo", Test::getFoo);
methods.set("Bar", Test::getBar);
// etc.
}
private String invokeGetter(String name) {
if (methods.containsKey(name)) {
return String.valueOf(methods.get(name).get());
} else {
throw new NoSuchMethodException();
}
}
It might sound like a major DRY violation to do so, but the repetition at least makes sure you don't wind up with unrelated getters accidentally called.
Class c = Test.class;
for(int i = 0; i < listOfAttributes.length; i++)
{
auxText += String.valueOf( c.getMethod( "get" + listOfAttributes[i]).invoke( this, null ) );
}
You can do this somewhat more elegantly via Java Beans, the Introspector, and PropertyDescriptor, but it's a little more long-winded:
Map<String, Method> methods = new HashMap<>();
Class c = this.getClass(); // surely?
for (PropertyDescriptor pd : Introspector.getBeanInfo(c).getPropertyDescriptors())
{
map.put(pd.getName(), pd.getReadMethod();
}
//
for (int i = 0; i < listOfAttributes.length; i++)
{
Method m = methods.get(listOfAttributes[i]);
if (m == null)
continue;
auxText += String.valueOf(m.invoke(this, null));
}
Related
public static void calculate(List<Person> data, String categoryType) {
for(int i = 0; i < categoryData.size(); i++) {
if(data.get(i).calculateCategoryOne() == firstPlace) {
...
}
}
}
If you see data.get(i).calculateCategoryOne(), the method call is for category one. The problem is that I need to copy-paste the entire code in a if-block for each category to just change this method call data.get(i).calculateCategoryTwo(), data.get(i).calculateCategoryThree(), ... data.get(i).calculateCategoryTen(),
While I can still make the logic work in this way, I feel it is redundant and not a good programming practice. Just to change one line of code, I would have to replicate the same code ten different times which will add nearly 500 lines of code.
So, my question is: Is there a way to dynamically change my method call based on the category type string argument.
I was thinking one possible way is to pass the method call in a string and convert it to a method call itself. For example, let's assume CategoryType string argument is "calculateCategoryOne()". So, data.get(i)."calculateCategoryOne()" would be recognized by the compiler as the method call itself. Is there a way to actually implement this?
I'm open to other ideas as well to reduce redundancy.
I would think using a functional interface would be appropriate here. You want different functionality depending on the categoryType, so passing in the function you want to use, rather than a String representation of it, would accomplish this.
#FunctionalInterface
public interface Calculate {
int calculate(Person data);
}
public static void calculate(List<Person> data, Calculate calculate) {
for(int i = 0; i < categoryData.size(); i++) {
if(calculate.calculate(data.get(i)) == firstPlace) {
...
}
}
}
and the call to the method would define what the calculation would be
calculate(list, p -> {
// calculation done here
});
or if this would happen frequently, you could predefine your categories once and pass those in:
Calculate categoryOne = p -> { ... };
Calculate categoryTwo = p -> { ... };
.
.
calculate(list, categoryOne);
In my app, I have a method that accepts an ArrayList of ID's and returns a RealmList of Machines belonging to these IDs.
public RealmList<Machine> getMachinesById(ArrayList<Long> machineIds) {
RealmList<Machine> machines = new RealmList<Machine>();
for (int i = 0; i < machineIds.size(); i++){
Machine m = getMachineById(machineIds.get(i));
if (m != null) {
machines.add(m);
}
}
return machines;
}
The getMachineById() function just finds the correct machine for a specific id.
I want to filter this output some more, however, when I try to get the RealmQuery by doing .where(), I get an Exception telling me I should put this RealmList in 'managed mode'.
Caused by: io.realm.exceptions.RealmException: This method is only available in managed mode
at io.realm.RealmList.where(RealmList.java:425)
I'm aware that I get this error because this list is standalone, and not managed by Realm.
It is probably important to add that this function will be called quite a lot, since it is triggered every time some list in my app refreshes. This would mean that (if possible) every time I'm creating a new managed RealmList.
My questions:
Is there any way to let this RealmList be managed by Realm?
If this is possible, is it a problem that this function is being called pretty often
Is there any other (preferred) way to achieve this (List of IDs > RealmResults/RealmQuery)
Is there any way to let this RealmList be managed by Realm?
Yes. There is. But the point of having RealmList is it should be a field of an RealmObjects. eg.:
public class Factory {
RealmList<Machine> machineList;
// setter & getters
}
Factory factory = new Factory();
RealmList<Machine> machineList = new RealmList<>();
// Add something to the list
factory.setMachines(machineList);
realm.beginTransaction();
Factory managedFactory = realm.copyToRealmOrUpdate(factory);
realm.commitTransaction();
Managed means it has be persisted Realm.
If this is possible, is it a problem that this function is being called pretty often
Depends, if you don't need to persist them again, see answer 3.
Is there any other (preferred) way to achieve this (List of IDs > RealmResults/RealmQuery)
In your case, maybe you can use ReaulResults instead? eg.:
RealmQuery<Machine> query = realm.where(Machine.class);
for (int i = 0; i < machineIds.size(); i++){
if (i != 0) query = query.or();
query = query.equalTo("id", machineIds.get(i));
}
RealmResults<Machine> machines = query.findAll();
I'm working on a small program to compare service levels, the user will input the service level 2 times (current and requested) and then the input will be scanned and compared and show a message.
For example:
current = 9*5 NBD (a)
requested = 24*7 SBD (b)
I want to know how in Java I can tell the compiler that (b) is greater than (a)
Because I want to use if statement like this
if (b > a) then show message.
I tried to use string.equals, but didn't help me too much.
I was not successful to convert string to number to do such comparison.
Try following statement
if(a.compareTo(b) > 0);
First thing: you can't override String.compareTo(), because it's final. you can create class with String field and write compareTo() for this class. This is not best idea.
But you can compare two strings by putting them into array and creating implementation of Comparator interface in sort() method.
String current = "9*5 NBD";
String requested = "24*7 SBD";
String[] test = {current, requested};
Arrays.sort(test, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
//Your impl goes here
return 0;
}
});
Where do these strings come from? Surely they must be from some kind of table that contains the service level details together with the cost of subscribing to that service level. What you want to check is whether the required service level costs more than the service level the client already has. Suppose the service level details come from a Map<ServiceLevel, BigDecimal> that gives the cost for a certain service level. Then all you need to do is:
BigDecimal costOfCurrentSL = serviceLevelCosts.get(currentSL);
BigDecimal costOfRequiredSL = serviceLevelCosts.get(requiredSL);
if (costOfRequiredSL.compareTo(costOfCurrentSL) > 0) {
// ... tell client he needs to purchase a top-up
}
Thank you all for the willing to help :)
After a lot of thinking I found another way which helped me a lot
I created 2 new integers and called them Values of what I need.
and used if statement, that if the entered is 9*5 NBD so the value will be zero, and if it is SBD, the value will be 1 and so on, then created new if statement to compare the values and show me a message if the A is greater than B, and it really worked.
Here is a part of my code
String WA_SLA = "", REQ_SLA = "";
int Va_WA_SLA = 0, Va_REQ_SLA = 0;
if(WA_SLA.equalsIgnoreCase("9*5 SBD"))
{
Va_WA_SLA = 1;
}
if(REQ_SLA.equalsIgnoreCase("9*5 NBD"))
{
Va_REQ_SLA = 0;
}
if(Va_WA_SLA > Va_REQ_SLA)
{
JOptionPane.showMessageDialog(frame,"Warranty SLA is Higher than Requested SLA " ,null, JOptionPane.INFORMATION_MESSAGE);
}
Thaaaaaaaaaaaaank you a lot
How to improve the performance of below code ,Can you please provide any hints to improve the code performance is it correct way to use CopyOnWriteList .Is there any chance below code could cause memory leaks.
code:
public static List<TestVO> description(final TestVO desc) {
List<TestVO> descList = new CopyOnWriteArrayList<TestVO>();
final StringBuilder builder = new StringBuilder();
String add = "";
TestVO desc =null;
for (int i = 0; i < 2; i++) {
desc = new TestVO();
for (String key : audit.getLog().keySet()) {
if (!key.equals("hello")) {
builder.append(key + "=" + audit.getLog().get(key)
+ add);
add = ", ";
audit.getLog().remove(key);
} else {
desc.setDesc(key + " = "
+ audit.getLog().get(key));
descList.add(desc);
audit.getLog().remove(key);
break;
}
}
}
desc.setDesc("Checks : " + builder.toString());
descList.add(desc);
return descList;
}
Calling from main method:
TestVO a1=new TestVO();
a1.getLog().put("1", new BigDecimal(12));
a1.getLog().put("2", new BigDecimal(22));
a1.getLog().put("3", new BigDecimal(32));
a1.getLog().put("4", new BigDecimal(42));
a1.getLog().put("Hello", new BigDecimal(90));
description(a1);
TestVO has 2 methods one is log its type is HashMap and other one is desc its type is string.
Performance Tips for That Particular Code
Lose the seemingly useless first loop.
Use StringBuilder the way it's meant to be:
builder.append(key + "=" + audit.getLog().get(key) + add);
Should really be:
builder.append(key).append("=").append(audit.getLog().get(key)).append(add);
Do you really need a descList as a List and not a Set?
You don't need a CopyOnWriteArrayList here anyways. Or at Least we can't know if you need one from this code.
General Tips for That Particular Code
It's very unclear what that does, it looks like a made up and contrived example. Is it one?
Your code seems to make multiple things at once. Maybe it's good for performance, but maybe that also gives it a pretty weird design.
The way you change your add variable seems rather odd and it looks like you'd be better off building a collection and then generating the strings you need.
auditDesc is undeclared, so you probably didn't give us the correct code.
you say calling from main method, but we don't see any calls to description() (Found it, wasn't visible as it was outside of the code block.)
We have no clue what it's supposed to do, so I can't really be of more help until your provide a more complete code sample.
I'm working with Flex 4 invoking webservices from JVM 1.6
I'm trying to make asynchronous calls to java to populate three comboboxes that will display country, states and cities, these three are related (master-slave relationship) but sometimes the calls are not met, or they are not completed, and I think this is because they are asynchronous, how can I be sure that I'm making the call to fill the next combobox when the master combobox (country) is filled?
protected function comboCountry_changeHandler(idCombo:String):void
{
selectedComboCountry= idCombo;
var countryId:String;
if(selectedComboCountry == comboCountry.id){
countryId = String(comboCountry.selectedItem.countryId);
}else if(selectedCombocountry == combocountrySuc.id){
countryId = String(comboCountrySuc.selectedItem.countryId);
}
obtainStatesResult.token = wsfacturas.obtainStates(countryId);
}
protected function obtainStatesResult_resultHandler(event:ResultEvent):void
{
var StateListVo:ArrayCollection = obtainStatesResult.token.result as ArrayCollection;
if(selectedComboCountry == "comboCountrySuc"){
StateListsSuc.removeAll();
CityListsSuc.removeAll();
for (var d:int = 0; d < StateListVo.length; d++){
var estSuc:State = StateListVo[d];
StateListsSuc.addItem(estSuc);
}
comboStateSuc.dataProvider = StateListsSuc;
}
else if(selectedCombocountry == "combocountry"){
StateListsEmp.removeAll();
CityListsEmp.removeAll();
for (var i:int = 0; i < StateListVo.length; i++){
var estEmp:State = StateListVo[i];
StateListsEmp.addItem(estEmp);
}
comboState.dataProvider = StateListsEmp;
} else {
for (var f:int = 0; f < StateListVo.length; f++){
var est:State = StateListVo[f];
StateListsSuc.addItem(est);
StateListsEmp.addItem(est);
}
comboState.dataProvider = StateListsEmp;
comboStateSuc.dataProvider = StateListsSuc;
}
}
Wouldn't it mean that you probably need to load the country and wait for change event on the country combobox to update state and so on? If you do that, you dont have to worry about asynchronicity of your requests? Otherwise, you might be able to use DataProviders and they might provide Event:Complete..
I am not sure though, I definitely am not an expert.. :)
You might want to give slightly more details in your question. But I am just playing with whatever details you have provided.
1_ First asynchronous calls never caused me any problem with dynamic data binding. Sometimes
a SOAP call can take reasonable amount of time, in that case, make sure to show a busy cursor till the web service call returns.
2_Combobox has always had problem with dynamic data binding. You can create a custom combobox that extends a combobox and override the setValue method Or Else you have to iterate over the dataProvider of the combobox and look for a match in the data field and then sets the combo box to that item.
So in summary, the problem mostly have to do with binding of the new data to the rendered combobox not asynchronous calls or any latency.