content: /*****************************************************************
   *   Licensed to the Apache Software Foundation (ASF) under one
   *  or more contributor license agreements.  See the NOTICE file
   *  distributed with this work for additional information
   *  regarding copyright ownership.  The ASF licenses this file
   *  to you 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
   *
   *    https://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 org.apache.cayenne.swing;
  
  import java.awt.Component;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import javax.swing.JTable;
  import javax.swing.table.AbstractTableModel;
  import javax.swing.table.TableCellRenderer;
  import javax.swing.table.TableColumn;
  import javax.swing.table.TableColumnModel;
  import javax.swing.table.TableModel;
  
  import org.apache.cayenne.util.Util;
  
  /**
   * A binding for a JTable.
   * 
   */
  public class TableBinding extends BindingBase {
  
      /**
       * A variable exposed in the context of set/get cell value.
       */
      public static final String ITEM_VAR = "item";
  
      protected JTable table;
      protected String[] headers;
      protected BindingExpression[] columns;
      protected boolean[] editableState;
      protected Class[] columnClass;
      protected List list;
  
      public TableBinding(JTable table, String listBinding, String[] headers,
              BindingExpression[] columns, Class[] columnClass, boolean[] editableState,
              Object[] sampleLongValues) {
  
          super(listBinding);
          this.table = table;
          this.headers = headers;
          this.columns = columns;
          this.editableState = editableState;
          this.columnClass = columnClass;
  
          table.setModel(new BoundTableModel());
          resizeColumns(sampleLongValues);
      }
  
      protected void resizeColumns(Object[] sampleLongValues) {
  
          TableCellRenderer headerRenderer = table.getTableHeader().getDefaultRenderer();
          TableColumnModel columnModel = table.getColumnModel();
          TableModel tableModel = table.getModel();
  
          for (int i = 0; i < columnModel.getColumnCount(); i++) {
  
              TableColumn column = columnModel.getColumn(i);
  
              Component header = headerRenderer.getTableCellRendererComponent(null, column
                      .getHeaderValue(), false, false, 0, 0);
              int headerWidth = header.getPreferredSize().width;
  
              if (sampleLongValues[i] != null) {
                  Component bigCell = table
                          .getDefaultRenderer(tableModel.getColumnClass(i))
                          .getTableCellRendererComponent(
                                  table,
                                  sampleLongValues[i],
                                  false,
                                  false,
                                  0,
                                  i);
                  int cellWidth = bigCell.getPreferredSize().width;
                  column.setPreferredWidth(Math.max(headerWidth, cellWidth));
              }
              else {
                  column.setPreferredWidth(headerWidth);
              }
          }
      }
  
      public void setContext(Object object) {
          super.setContext(object);
  
          this.list = updateList();
      }
  
      public Component getView() {
          return table;
      }
  
      public void updateView() {
          this.list = updateList();
          ((BoundTableModel) table.getModel()).fireTableDataChanged();
      }
  
      int getListSize() {
          return (list != null) ? list.size() : 0;
      }
  
      List updateList() {
          if (getContext() == null) {
              return null;
          }
  
          Object list = getValue();
          if (list == null) {
              return null;
          }
  
          if (list instanceof List) {
              return (List) list;
          }
  
          if (list instanceof Object[]) {
              Object[] objects = (Object[]) list;
              return Arrays.asList(objects);
          }
  
          if (list instanceof Collection) {
              return new ArrayList((Collection) list);
          }
  
          throw new BindingException("List expected, got - " + list);
      }
  
      final class BoundTableModel extends AbstractTableModel {
  
          // this map is used as "flyweight", providing on the spot context for Ognl
          // expression evaluation
          Map<String, Object> listContext = new HashMap<String, Object>(){{put(ITEM_VAR, null);}};
  
          public int getColumnCount() {
              return headers.length;
          }
  
          public int getRowCount() {
              return getListSize();
          }
  
          public boolean isCellEditable(int rowIndex, int columnIndex) {
              return editableState[columnIndex];
          }
  
          public Object getValueAt(int rowIndex, int columnIndex) {
              Object item = list.get(rowIndex);
              listContext.put(ITEM_VAR, item);
              return columns[columnIndex].getValue(getContext(), listContext);
          }
  
          public String getColumnName(int column) {
              // per CAY-513 - if an empty string is passed for header, table header will
              // have zero height on Windows... So we have to check for this condition
              return Util.isEmptyString(headers[column]) ? " " : headers[column];
          }
  
          public Class getColumnClass(int columnIndex) {
              return columnClass[columnIndex];
          }
  
          public void setValueAt(Object value, int rowIndex, int columnIndex) {
              Object item = list.get(rowIndex);
              listContext.put(ITEM_VAR, item);
              columns[columnIndex].setValue(getContext(), listContext, value);
          }
      }
  }
  