I'm now looking into JTables and have a bunch of business objects that I retrieve from the DB with Hibernate + Spring Data JPA.
I love that Spring Data JPA handles all the cumbersome implementation of the DAL and was wondering if there's something similar for TableModel.
Basically, I would have something along the lines of:
public class GenericTableModel<T> extends AbstractTableModel
And the GenericTableModel would use reflection and/or annotations to look into T.
Does something like this exist? I hope I don't have to have a TableModel for each object I want to display on a JTable..
And the GenericTableModel would use reflection to look into T.
The Bean Table Model does this.
http://java.net/projects/beansbinding/
Will let you bind business objects to view components (including tables) as long as you have Bean-Style getters and setters.
I am in the process of writing a library for Swing Developers. Its a work in progress. But i have done what you are looking for. Take a look at the classes in this package:
http://code.google.com/p/swingobjects/source/browse/#git%2FSwingObjects%2Fsrc%2Forg%2Faesthete%2Fswingobjects%2Fview%2Ftable
For an example of how to use this, please take a look at this class- Look at line numbers - 70-85
http://code.google.com/p/swingobjects/source/browse/SwingObjects/src/test/CompTest.java
I havent got the documentation written yet. But if you dont follow anything, please comment back here.
Update - Code sample
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import org.aesthete.swingobjects.annotations.Column;
import org.aesthete.swingobjects.view.table.RowDataBean;
import org.aesthete.swingobjects.view.table.SwingObjTable;
import org.jdesktop.swingx.JXFrame;
public class TableDemo {
public static void main(String[] args) {
try {
//For this demo the Framework need not be initialised.. If you plan on using the entire framework, then
//its best you initialise it before working on anything...
// SwingObjectsInit.init("/swingobjects.properties", "/error.properties");
//Here's the data to show on the table
final List<Row> rows = new ArrayList<Row>();
rows.add(new Row("Data 1", "Data 2", "Yes", true));
rows.add(new Row("Data 3", "Data 4", "No", false));
//Create the swing table as below.. Provide the Row.class to say that the data in the rows
// will be from this class
final SwingObjTable<Row> table = new SwingObjTable<Row>(Row.class);
table.setData(rows);
table.setVisibleRowCount(4);
//Make any column into a combo box by calling the below method.
//A column can be automatically made into a checkbox, by defining your property in the Row class as a boolean
table.makeColumnsIntoComboBox(new String[] { "Yes", "No" }, 2);
//Initialise the frame and show it on the screen
final JXFrame frame = new JXFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new JScrollPane(table));
frame.pack();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
public static class Row extends RowDataBean{
#Column(index=0,name="Column 1",editable=true)
private String column1;
#Column(index=1,name="Column 2",editable=true)
private String column2;
#Column(index=2,name="Column 3",editable=true)
private String column3;
#Column(index=3,name="Column 4",editable=true)
private boolean column4;
public Row(String column1, String column2, String column3, boolean column4) {
super();
this.column1 = column1;
this.column2 = column2;
this.column3 = column3;
this.column4 = column4;
}
public String getColumn1() {
return column1;
}
public void setColumn1(String column1) {
this.column1 = column1;
}
public String getColumn2() {
return column2;
}
public void setColumn2(String column2) {
this.column2 = column2;
}
public String getColumn3() {
return column3;
}
public void setColumn3(String column3) {
this.column3 = column3;
}
public boolean getColumn4() {
return column4;
}
public void setColumn4(boolean column4) {
this.column4 = column4;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((column1 == null) ? 0 : column1.hashCode());
result = prime * result + ((column2 == null) ? 0 : column2.hashCode());
result = prime * result + ((column3 == null) ? 0 : column3.hashCode());
result = prime * result + (column4 ? 1231 : 1237);
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Row other = (Row) obj;
if (column1 == null) {
if (other.column1 != null)
return false;
} else if (!column1.equals(other.column1))
return false;
if (column2 == null) {
if (other.column2 != null)
return false;
} else if (!column2.equals(other.column2))
return false;
if (column3 == null) {
if (other.column3 != null)
return false;
} else if (!column3.equals(other.column3))
return false;
if (column4 != other.column4)
return false;
return true;
}
}
}
Update: Answer to queries in Comments
1) The table can be inserted in any Component, right? (JPanel, JScrollPane, etc.),
Yes. The table extends JXTable (swingx) which extends JTable. So it can be placed inside any component. You would ideally place into a JScrollPane though.
2) We have control over the column names? (my app is localized in several langs)
Thanks for this. I didnt think about localisation when I started making the framework. You pointed me in the right direction. I was able to modify the framework quickly to achieve this. Its easy actually:
To make use of l10n - Before you start coding, make sure you initialize the Swing Objects Framework with the below line. If you dont provide a Locale, the default Locale will be applied.
SwingObjectsInit.init("swingobjects", "application",new Locale("fr", "FR"));
You need to have to have 2 sets of properties file.
1) swingobjects - This provides the defaults for the swingobjects framework. The file is already provided in my code base. Just copy paste the file onto a classpath location.
2) application - This is where you will need to put in your application's GUI texts/messages. You will have to make a new application properties file for every Locale you need.
Then finally, instead of saying
#Column(index=0,name="Column 1",editable=true)
private String column1;
You will have to use:
#Column(index=0,key="test.column1",editable=true)
private String column1;
Change name to key. This will make it read the Resource Bundle and search for a property test.column1 rather than your hard coded column Name.
3) Does SwingObjTable require hashCode and equals to be implemented?
The equals and hashcode is required if you use the Swing Objects Framework in its entirety. The Swing Objects Framework, allows you to set data for the GUI elements in a bean in your model class - Read as in MVC. The framework, then automatically updates the GUI if the bean's value has changed. In order to check if the bean's value has indeed changed, it needs to call the equals method. Hence you need to override it. You can make Eclipse generate this for you. Or if you dont use anything in the framework, then just call super.equals() from there.
Just check out / clone the git repo and you'll have be access the TableDemo example..
git clone https://writetosethu#code.google.com/p/swingobjects/
Related
I have a DAOImplementation class with the method definition below.
#Override
public Registration getRegistrationInfoById(int aRegistrationId) {
String SQL = "{CALL getRegistrationInfoById(?)}";
Registration aRegistration = new Registration();
try (Connection con = DBUtil.getConnection(DBType.MYSQL);
CallableStatement cs = con.prepareCall(SQL);) {
cs.setInt(1, aRegistrationId);
try (ResultSet rs = cs.executeQuery();) {
while (rs.next()) {
int gradeLevel = Integer.parseInt(rs.getString(RegistrationTable.GRADELEVEL));
aRegistration.setGradeLevel(gradeLevel);
}
}
} catch (SQLException e) {
JOptionPane.showMessageDialog(null, e.getErrorCode() + "\n" + e.getMessage());
}
return aRegistration;
}//end of method
This returns an integer value of Grade Level (1,2,3,4,5,6,7...so on...) which I've verified because I tried printing the output returned by aRegistration.getGradeLevel();
Now my problem is with my JComboBox. I have set a ListCellRenderer for my JComboBox which holds all the GradeLevel values
public class JComboBoxRenderer_GradeLevel extends JLabel implements ListCellRenderer<Object> {
public JComboBoxRenderer_GradeLevel() {
this.setOpaque(true);
}
#Override
public Component getListCellRendererComponent(JList<? extends Object> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
if (value instanceof GradeLevel) {
this.setText("" + ((GradeLevel) value).getGradelevel());
} else {
this.setText("--");
}
if (isSelected) {
this.setBackground(Color.YELLOW);
this.setForeground(list.getSelectionForeground());
} else {
this.setBackground(list.getBackground());
this.setForeground(list.getForeground());
}
return this;
}
}
And looks like this JComboBox as expected. (GradeLevel model is renderered to simply show an int value of gradelevel), ((GradeLevel) value).getGradelevel());returns an integer value.
I understand that even when JComboBox has its renderer that displays an integer value of GradeLevel by using ((GradeLevel)value).getGradeLevel(), the actual value on the JComboBox is still treated as instance of GradeLevel or object. But not a String or int.
So my problem is when I try to set the selected value to an int value, it won't change the selected value of the JComboBox. Nothing happens when I use setSelectedItem();
This is what I tried to do for the GUI.
//Grade Level
GradeLevelDaoImpl gldi = new GradeLevelDaoImpl();
List<GradeLevel> gradeLevels = gldi.getAllGradeLevelsInfo();
DefaultComboBoxModel gradeLevelModel = new DefaultComboBoxModel(gradeLevels.toArray());
jcmbGradeLevel.setModel(gradeLevelModel);
jcmbGradeLevel.setRenderer(new JComboBoxRenderer_GradeLevel());
jcmbGradeLevel.setSelectedIndex(-1);
GradeLevel gradeLevel = new GradeLevel();
gradeLevel.setGradelevel(registration.getGradeLevel());
jcmbGradeLevel.setSelectedItem(gradeLevel); //PROBLEM HERE, it doesn't change
JOptionPane displays this.
JOptionPane.showMessageDialog(null,"GradeLevel: "+gradeLevel);
JOptionPane.showMessageDialog(null,"GradeLevel: "+gradeLevel.getGradeLevel());
It doesn't seem to be able to compare the object I'm trying to set it to(gradeLevel) with the objects JComboBox has(gradeLevels). Notice the singular and plural.
How do I manipulate the types so that setSelectedItem() will match with what the JComboBox have?
Thanks.
If you want to do this by using different instances of the object, but with the same properties, then you need to override the class's equals and hashcode methods, so that the combination of properties are unique. This is very important, this is a relationship expectation that any object which is equal to another will have the same hashcode
This is a really quick example and I used by IDE's auto generation process (because I'm lazy), but, if your Registration class has other properties which should be considered when comparing to instances of the class, you will need to modify it to support them (again, any good IDE should be able to do this)
public class Registration {
private int gradeLevel;
public Registration(int gradeLevel) {
this.gradeLevel = gradeLevel;
}
public int getGradeLevel() {
return gradeLevel;
}
public void setGradeLevel(int gradeLevel) {
this.gradeLevel = gradeLevel;
}
#Override
public int hashCode() {
int hash = 7;
hash = 73 * hash + this.gradeLevel;
return hash;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Registration other = (Registration) obj;
if (this.gradeLevel != other.gradeLevel) {
return false;
}
return true;
}
}
Then using something like...
Registration a = new Registration(1);
Registration b = new Registration(1);
Registration c = new Registration(2);
System.out.println(a.equals(b));
System.out.println(a.equals(c));
System.out.println(b.equals(c));
will print...
true
false
false
which shows us that the code is working.
Once you get this setup, you should then be able to change the selected item by creating an instance of Registration, seeding it with the required properties and passing it to the JComboBox.
This is very important and very common concept used a lot within Java, well worth taking the time to learn and understand
I'm trying to filter Rows in a JTable which contains Columns with numbers.
The filtering is working so far, but it filters over the numbers including the thousands-separators. For example, if there is a row with the number 25689 in one row and I try to filter for this row, i have to use "25.689". So it seems there is a formatting that is performed before the filtering.
I've tried to set an own default renderer and the numbers are shown without the separators but the filtering is the same.
Edit
I've added a full example re-creating my problem:
public class GroupingTest {
JFrame frame= null;
Container pane= null;
JTextField tf=null;
JXTable table=null;
public void searchTable() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
final String searchEx = "(?i)"
+ Pattern.quote(tf.getText());
final RowFilter<TableModel, Object> filter;
filter = RowFilter.regexFilter(searchEx);
table.setRowFilter(filter);
//packAll in edt
Utility.packTableView(table);
} catch (final Exception e) {
return;
}
}
});
}
public void createTable() {
frame = new JFrame();
pane=frame.getContentPane();
tf = new JTextField();
tf.setPreferredSize(new Dimension(200,25));
tf.getDocument().addDocumentListener(new DocumentListener() {
#Override
public void removeUpdate(final DocumentEvent e) {
searchTable();
}
#Override
public void insertUpdate(final DocumentEvent e) {
searchTable();
}
#Override
public void changedUpdate(final DocumentEvent e) {
searchTable();
}
});
String[] columnHeaders = {"long","strings"};
DefaultTableModel $model = new DefaultTableModel(columnHeaders, 0) {
#Override
public Class<?> getColumnClass(final int $col) {
if($col == 0) {
return Long.class;
} else if($col == 1){
return String.class;
} else {
return Object.class;
}
}
};
table = new JXTable($model);
table.setDefaultRenderer(Long.class, new DefaultTableCellRenderer() {
#Override
public java.awt.Component getTableCellRendererComponent(final JTable $table,
final Object $value, final boolean $isSelected, final boolean $hasFocus, final int $row,
final int $column) {
super.getTableCellRendererComponent($table, $value, $isSelected, $hasFocus, $row, $column);
if ($value instanceof Long) {
this.setHorizontalAlignment(SwingConstants.RIGHT);
}
return this;
}
});
Object[] line1 = {new Long(23345),"asdf"};
$model.addRow(line1);
Object[] line2 = {new Long(3),"dfw"};
$model.addRow(line2);
pane.add(tf,BorderLayout.NORTH);
pane.add(new JScrollPane(table),BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(300,200));
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
GroupingTest gt = new GroupingTest();
gt.createTable();
}
}
The Filtering is working so far, but it filters over the numbers including the thousands-separators.
When the value's format interferes with the expected functioning of sorters and filters then it's time to check if getColumnClass(int columnIndex) in the table model is retrieving the appropriate class (in this case Double).
By default AbstractTableModel implementation of such method returns Object.class which is rendered using the toString() method (that's why you see the thousands-separator) and probably filtered according to the string representation as well. Subclasses of AbstractTableModel (such as DefaultTableModel) inherit this implementation and thus this method should be overriden. For example let's say your table model is DefaultTableModel and the first column is a Double:
DefaultTableModel model = new DefaultTableModel() {
#Override
public Class<?> getColumnClass(int columnIndex) {
return columnIndex == 0 ? Double.class
: super.getColumnClass(columnIndex);
}
};
See Sorting and Filtering section of How to Use Tables tutorial for further details.
Update
Given your new MVCE it is clear now what are you trying to achieve. I'd start saying that I've mistakenly assumed your table model holds Double instead of Long which makes no difference about overriding getColumnClass() method (it should be done anyways) but it will make a slight difference in the final solution.
Now, to state the requirements clear, you need to filter that column either:
Users input a number (Long) including grouping character.
Users input a number without grouping character.
The string representation of the value contains the substring typed by the users.
To achieve this goal I'd use a custom RowFilter instead of using a regex filter like you do in your example. This is to have control about the string typed by the user and check the three conditions listed above. I've managed to modify your searchTable() to satisfy the requirements. Note: I've included the queried String as an argument in this method to keep tf text field out of the implementation. Please see the code below:
private void searchTable(final String query) {
RowFilter<TableModel, Integer> filter = null;
if (query.length() > 0) {
filter = new RowFilter<TableModel, Integer>() {
#Override
public boolean include(RowFilter.Entry<? extends TableModel, ? extends Integer> entry) {
for (int i = 0; i < entry.getValueCount(); i++) {
String stringValue = entry.getStringValue(i);
Object entryValue = entry.getValue(i);
String numberString = entryValue instanceof Long
? String.valueOf(entryValue)
: "";
if (stringValue.contains(query) || numberString.contains(query)) {
return true;
}
}
return false;
}
};
}
table.setRowFilter(filter);
}
The flow will be more or less as follows:
If the query length is 0 just let the filter be null. This means the table won't be filtered and all rentries will be included.
If not (1) then prepare a new filter which iterates over the whole row asking if the String representation of the entry or the String value of the entry contains the queried String. While those might look the same thing they are not because Entry#getStringValue(int index) might (and actually does) retrieve a different value than String#valueOf(entry#getValue(int index)). In this case the first one retrieves the Long including grouping separators (or formatted if you prefer) while the second one retrieves the Long with no formatting at all (it means, no grouping separators).
Apply the filter to the table in either case.
I hope the idea is clear enough. If you want to filter a Double then it has to be tweaked a little bit because String.valueOf(double) includes the decimal (not grouping) separator and you might want to remove it before checking if it contains the queried String.
I need to dinamically add columns to GXT grid. I can do that, but problem occurs, when I want to input data for rows. Thing is, that not all rows have specific column. So what I want to achieve is to check if given row has specific column and return proper value.
Problem is, that ValueProvider for my column doesn't allow to use arguments in it's methods. So I can't pass column name to ValueProvider, so it could check if given column exists in specific row and return proper data.
Here is my column:
ColumnConfig<SomeClass, String> column = new ColumnConfig<SomeClass, String> (props.attributeValue(name), 150, name);
Here is my ValueProvider
ValueProvider<LimitDTO, String> attributeValue(String name);
And here is my implementation (simplified):
public String getAttributeValue(String name) {
if(this.attributes.get(name) == null) {
return "";
} else {
return this.attributes.get(name);
}
}
But I get build error:
Method public abstract com.sencha.gxt.core.client.ValueProvider<com.example.SomeClass, java.lang.String> attributeValue(java.lang.String s) must not have parameters
SOLUTION
Thanks to your answers I was able to do it. This is my implementation of ValueProvider in case someone will look for solution. It wasn't so hard after all :)
public class CustomValueProvider implements ValueProvider<SomeClass, String> {
public String column;
public CustomValueProvider(String column) {
this.column = column;
}
#Override
public String getValue(SomeClass object) {
if(object.getAttributes().get(column) == null) {
return "";
} else {
return object.getAttributes().get(column);
}
}
#Override
public void setValue(SomeClass object, String value) {
}
#Override
public String getPath() {
return column.getName();
}
}
And here is how I used it
LimitsValueProvider lvp = new LimitsValueProvider(name);
ColumnConfig<SomeClass, String> newColumn = new ColumnConfig<>(lvp, 150, name);
Thanks a lot!
I would suggest, do not use
props.attributeValue(name)
Instead, you can follow the post Dynamic charts in GXT 3 and you can create your own dynamic value providers (See the section value providers), which will take columnId (path) as input and peform the same functionality.
Remember ValueProvider is just an interface and using GWT.create you provides its default implementation.
I have a JTextField and a JList in my program. The JList contains the user's contacts. I'd like to filter the JList based on the text on the JTextField. For example, if I type in "Mike" it will only show contacts including "Mike". When the user clears the JTextField it would reset the filter.
I know I could do this manually by having two arrays. One for the original contacts and one for the filtered ones. When the user changes the value of the JTextField I would go trought the original list, update the temporary list and update the JList. I just wonder if there is some built in feature to avoid manual labour.
The best way to do things like that is to have a ListModel implementation that filters its contents.
I don't know of any default filtering ListModel implementations, but it should not be too hard to do.
Here's a quick and dirty solution just to give you an idea. You might want to add more bells and whistles to it.
package test;
import java.util.ArrayList;
import javax.swing.AbstractListModel;
import javax.swing.ListModel;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
public class FilteredListModel extends AbstractListModel {
public static interface Filter {
boolean accept(Object element);
}
private final ListModel _source;
private Filter _filter;
private final ArrayList<Integer> _indices = new ArrayList<Integer>();
public FilteredListModel(ListModel source) {
if (source == null)
throw new IllegalArgumentException("Source is null");
_source = source;
_source.addListDataListener(new ListDataListener() {
public void intervalRemoved(ListDataEvent e) {
doFilter();
}
public void intervalAdded(ListDataEvent e) {
doFilter();
}
public void contentsChanged(ListDataEvent e) {
doFilter();
}
});
}
public void setFilter(Filter f) {
_filter = f;
doFilter();
}
private void doFilter() {
_indices.clear();
Filter f = _filter;
if (f != null) {
int count = _source.getSize();
for (int i = 0; i < count; i++) {
Object element = _source.getElementAt(i);
if (f.accept(element)) {
_indices.add(i);
}
}
fireContentsChanged(this, 0, getSize() - 1);
}
}
public int getSize() {
return (_filter != null) ? _indices.size() : _source.getSize();
}
public Object getElementAt(int index) {
return (_filter != null) ? _source.getElementAt(_indices.get(index)) : _source.getElementAt(index);
}
}
In order to use it you need to set it to your JList and then call setFilter() as you need.
Here's an example:
ListModel source = new DefaultListModel(); // use a model of your choice here;
FilteredListModel filteredListModel = new FilteredListModel(source);
JList list = new JList(filteredListModel);
filteredListModel.setFilter(new FilteredListModel.Filter() {
public boolean accept(Object element) {
return false; // put your filtering logic here.
}
});
Once method setFilter() is invoked your JList on the screen is expected to change its contents accordingly.
Alternatively, you may want to implement an observer/observable pattern for your Filter, so you can re-filter the list without calling method setFilter(). You can experiment with that later. For the first iteration it's good enough as long as you call method setFilter every time user types something in your JTextField.
A simpler solution might be to use JTable, which does have a built-in ability to filter and sort (RowSorter). A single-column table is not too different from a list.
If you're okay with external libs, I would recommend Jide's QuickListFilterField/QuickTreeFilterField. With few lines of code, you could get a visually filterable JList/JTree, case sensitive/insensitive search, wildcard/regex matching etc ... Amazingly easy to use !
At the moment I have this code (and I don't like it):
private RenderedImage getChartImage (GanttChartModel model, String title,
Integer width, Integer height,
String xAxisLabel, String yAxisLabel,
Boolean showLegend) {
if (title == null) {
title = "";
}
if (xAxisLabel == null) {
xAxisLabel = "";
}
if (yAxisLabel == null) {
yAxisLabel = "";
}
if (showLegend == null) {
showLegend = true;
}
if (width == null) {
width = DEFAULT_WIDTH;
}
if (height == null) {
height = DEFAULT_HEIGHT;
}
...
}
How can I improve it?
I have some thoughts about introducing an object which will contain all these parameters as fields and then, maybe, it'll be possible to apply builder pattern. But still don't have clear vision how to implement that and I'm not sure that it's worth to be done. Any other ideas?
So many parameters to a method is definitely a code smell. I would say a Chart object is waiting to be born. Here is a basic outline:
private RenderImage getChartImage(Chart chart) {
//etc.
}
private static class Chart {
private GanttChartModel model;
private String title = "";
//etc, initializing each field with its default value.
private static class Builder {
private Chart chart;
public Builder(GanttChartModel model) {
chart = new Chart();
chart.model = model;
}
public setTitle(String title) {
if (title != null) {
chart.title = title;
}
}
}
}
Other options include using primitives on the methods instead of objects to indicate that null isn't allowed, although that doesn't necessarily make it better. Another option is a bunch of overloaded methods, but given the types of parameters here, that doesn't really work because I get the idea that you want to make any of the parameters optional rather than having the first ones required and subsequent ones optional.
Your method's purpose is to construct a complex object. Therefore, the builder pattern seems appropriate to solve this problem. A builder can manage many options for the creation of an object.
Some properties of the image should not have a default value. For example, an image without a title is not very useful, but this depends on the needs of your application.
The use of a builder could look like:
RenderedImage image = RenderedImageBuilder.getNew(model)
.title("title").width(100).height(100)
.showLegend().build();
A further advantage of builders is that they make it easy to document any defaults for parameters and how they should be used.
The best I can think of off hand is to introduce a Parameter Object (which will also be a builder) called something like ChartOptions to contain all the options for this method.
The object could be built piecemeal:
ChartOptions options = new ChartOptions()
.setHeight(10)
.setWidth(100)
getChartImage(model, options);
etc.
If that doesn't work you can at least encapsulate the null check:
private <A> A checkNull(A object, A default)
{
return object == null ? default : object;
}
I would move that logic into the setter methods of the class you're returning an object of.
public class MyRenderedImage implements RenderedImage {
public MyRenderedImage(String title, ...) {
// constructor should call setters that do validation/coercion
}
public void setTitle(String title) {
if (title == null) {
this.title = "";
}
}
...
}
Another option to consider is to throw an InvalidArgumentException, but it sounds like you already know what you want to do.
Well, I'm thinking about that is there some framework support #NotNull annotation, if a method has this annotation, the framework will check all it's parameters.
#NotNull
public void doSomething(Parameter a, Parameter b) {
}
You can have a map values initially constructed. You can then do something like this,
private RenderedImage getChartImage(GanttChartModel model, String title,
Integer width, Integer height, String xAxisLabel,
String yAxisLabel, Boolean showLegend) {
title = removeNull(KEY_TITLE,title);
xAxisLabel = removeNull(KEY_X,xAxisLabel);
yAxisLabel = removeNull(KEY_Y,yAxisLabel);
showLegend = removeNull(KEY_LEG,showLegend);
width = removeNull(KEY_W,width);
height = removeNull(KEY_H,height);
}
//initialize the defaultMap with the key-value of default pairs
Map<Object,Object> defaultMap;
private Object removeNull(Object keyTitle, Object value) {
if(value==null){
return defaultMap.get(keyTitle);
}
return value;
}