blob: 5209c51ea5a044aad87e3346aeb6c8527ed7b408 [file] [log] [blame]
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.uiDesigner.wizard;
import com.intellij.ide.wizard.StepAdapter;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiType;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.uiDesigner.UIDesignerBundle;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableColumn;
import java.awt.*;
import java.util.ArrayList;
import java.util.Collections;
/**
* @author Anton Katilin
* @author Vladimir Kondratyev
*/
final class BindToExistingBeanStep extends StepAdapter{
private static final Logger LOG = Logger.getInstance("#com.intellij.uiDesigner.wizard.BindToExistingBeanStep");
private JScrollPane myScrollPane;
private JTable myTable;
private final WizardData myData;
private final MyTableModel myTableModel;
private JCheckBox myChkIsModified;
private JCheckBox myChkGetData;
private JCheckBox myChkSetData;
private JPanel myPanel;
BindToExistingBeanStep(@NotNull final WizardData data) {
myData = data;
myTableModel = new MyTableModel();
myTable.setModel(myTableModel);
myTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
myTable.getColumnModel().setColumnSelectionAllowed(true);
myScrollPane.getViewport().setBackground(myTable.getBackground());
myTable.setSurrendersFocusOnKeystroke(true);
// Customize "Form Property" column
{
final TableColumn column = myTable.getColumnModel().getColumn(0/*Form Property*/);
column.setCellRenderer(new FormPropertyTableCellRenderer(myData.myProject));
}
// Customize "Bean Property" column
{
final TableColumn column = myTable.getColumnModel().getColumn(1/*Bean Property*/);
column.setCellRenderer(new BeanPropertyTableCellRenderer());
final MyTableCellEditor cellEditor = new MyTableCellEditor();
column.setCellEditor(cellEditor);
final DefaultCellEditor editor = (DefaultCellEditor)myTable.getDefaultEditor(Object.class);
editor.setClickCountToStart(1);
myTable.setRowHeight(cellEditor.myCbx.getPreferredSize().height);
}
myChkGetData.setSelected(true);
myChkGetData.setEnabled(false);
myChkSetData.setSelected(true);
myChkSetData.setEnabled(false);
myChkIsModified.setSelected(myData.myGenerateIsModified);
}
public JComponent getComponent() {
return myPanel;
}
public void _init() {
// Check that data is correct
LOG.assertTrue(!myData.myBindToNewBean);
LOG.assertTrue(myData.myBeanClass != null);
myTableModel.fireTableDataChanged();
}
public void _commit(boolean finishChosen) {
// Stop editing if any
final TableCellEditor cellEditor = myTable.getCellEditor();
if(cellEditor != null){
cellEditor.stopCellEditing();
}
myData.myGenerateIsModified = myChkIsModified.isSelected();
// TODO[vova] check that at least one binding field exists
}
private final class MyTableModel extends AbstractTableModel{
private final String[] myColumnNames;
public MyTableModel() {
myColumnNames = new String[]{
UIDesignerBundle.message("column.form.field"),
UIDesignerBundle.message("column.bean.property")};
}
public int getColumnCount() {
return myColumnNames.length;
}
public String getColumnName(final int column) {
return myColumnNames[column];
}
public int getRowCount() {
return myData.myBindings.length;
}
public boolean isCellEditable(final int row, final int column) {
return column == 1/*Bean Property*/;
}
public Object getValueAt(final int row, final int column) {
if(column == 0/*Form Property*/){
return myData.myBindings[row].myFormProperty;
}
else if(column == 1/*Bean Property*/){
return myData.myBindings[row].myBeanProperty;
}
else{
throw new IllegalArgumentException("unknown column: " + column);
}
}
public void setValueAt(final Object value, final int row, final int column) {
LOG.assertTrue(column == 1/*Bean Property*/);
final FormProperty2BeanProperty binding = myData.myBindings[row];
binding.myBeanProperty = (BeanProperty)value;
}
}
private final class MyTableCellEditor extends AbstractCellEditor implements TableCellEditor{
private final ComboBox myCbx;
/* -1 if not defined*/
private int myEditingRow;
public MyTableCellEditor() {
myCbx = new ComboBox();
myCbx.setEditable(true);
myCbx.setRenderer(new BeanPropertyListCellRenderer());
myCbx.registerTableCellEditor(this);
final JComponent editorComponent = (JComponent)myCbx.getEditor().getEditorComponent();
editorComponent.setBorder(null);
myEditingRow = -1;
}
/**
* @return whether it's possible to convert <code>type1</code> into <code>type2</code>
* and vice versa.
*/
private boolean canConvert(@NonNls final String type1, @NonNls final String type2){
if("boolean".equals(type1) || "boolean".equals(type2)){
return type1.equals(type2);
}
else{
return true;
}
}
public Component getTableCellEditorComponent(
final JTable table,
final Object value,
final boolean isSelected,
final int row,
final int column
) {
myEditingRow = row;
final DefaultComboBoxModel model = (DefaultComboBoxModel)myCbx.getModel();
model.removeAllElements();
model.addElement(null/*<not defined>*/);
// Fill combobox with available bean's properties
final String[] rProps = PropertyUtil.getReadableProperties(myData.myBeanClass, true);
final String[] wProps = PropertyUtil.getWritableProperties(myData.myBeanClass, true);
final ArrayList<BeanProperty> rwProps = new ArrayList<BeanProperty>();
outer: for(int i = rProps.length - 1; i >= 0; i--){
final String propName = rProps[i];
if(ArrayUtil.find(wProps, propName) != -1){
LOG.assertTrue(!rwProps.contains(propName));
final PsiMethod getter = PropertyUtil.findPropertyGetter(myData.myBeanClass, propName, false, true);
if (getter == null) {
// possible if the getter is static: getReadableProperties() does not filter out static methods, and
// findPropertyGetter() checks for static/non-static
continue;
}
final PsiType returnType = getter.getReturnType();
LOG.assertTrue(returnType != null);
// There are two possible types: boolean and java.lang.String
@NonNls final String typeName = returnType.getCanonicalText();
LOG.assertTrue(typeName != null);
if(!"boolean".equals(typeName) && !"java.lang.String".equals(typeName)){
continue;
}
// Check that the property is not in use yet
for(int j = myData.myBindings.length - 1; j >= 0; j--){
final BeanProperty _property = myData.myBindings[j].myBeanProperty;
if(j != row && _property != null && propName.equals(_property.myName)){
continue outer;
}
}
// Check that we conver types
if(
!canConvert(
myData.myBindings[row].myFormProperty.getComponentPropertyClassName(),
typeName
)
){
continue;
}
rwProps.add(new BeanProperty(propName, typeName));
}
}
Collections.sort(rwProps);
for (BeanProperty rwProp : rwProps) {
model.addElement(rwProp);
}
// Set initially selected item
if(myData.myBindings[row].myBeanProperty != null){
myCbx.setSelectedItem(myData.myBindings[row].myBeanProperty);
}
else{
myCbx.setSelectedIndex(0/*<not defined>*/);
}
return myCbx;
}
public Object getCellEditorValue() {
LOG.assertTrue(myEditingRow != -1);
try {
// our ComboBox is editable so its editor can contain:
// 1) BeanProperty object (it user just selected something from ComboBox)
// 2) java.lang.String if user type something into ComboBox
final Object selectedItem = myCbx.getEditor().getItem();
if(selectedItem instanceof BeanProperty){
return selectedItem;
}
else if(selectedItem instanceof String){
final String fieldName = ((String)selectedItem).trim();
if(fieldName.length() == 0){
return null; // binding is not defined
}
final String fieldType = myData.myBindings[myEditingRow].myFormProperty.getComponentPropertyClassName();
return new BeanProperty(fieldName, fieldType);
}
else{
throw new IllegalArgumentException("unknown selectedItem: " + selectedItem);
}
}
finally {
myEditingRow = -1; // unset editing row. So it's possible to invoke this method only once per editing
}
}
}
}