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
   *
   * 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 org.apache.chemistry.opencmis.workbench;
  
  import java.awt.datatransfer.DataFlavor;
  import java.awt.datatransfer.Transferable;
  import java.awt.datatransfer.UnsupportedFlavorException;
  import java.awt.event.ActionEvent;
  import java.awt.event.ActionListener;
  import java.awt.event.KeyEvent;
  import java.awt.event.KeyListener;
  import java.awt.event.MouseAdapter;
  import java.awt.event.MouseEvent;
  import java.io.File;
  import java.io.IOException;
  import java.util.Collections;
  import java.util.EnumMap;
  import java.util.GregorianCalendar;
  import java.util.List;
  import java.util.Map;
  
  import javax.swing.DropMode;
  import javax.swing.Icon;
  import javax.swing.ImageIcon;
  import javax.swing.JComponent;
  import javax.swing.JMenuItem;
  import javax.swing.JOptionPane;
  import javax.swing.JPopupMenu;
  import javax.swing.JTable;
  import javax.swing.ListSelectionModel;
  import javax.swing.SwingUtilities;
  import javax.swing.TransferHandler;
  import javax.swing.event.ListSelectionEvent;
  import javax.swing.event.ListSelectionListener;
  import javax.swing.table.AbstractTableModel;
  import javax.swing.table.TableColumn;
  
  import org.apache.chemistry.opencmis.client.api.CmisObject;
  import org.apache.chemistry.opencmis.client.api.Document;
  import org.apache.chemistry.opencmis.client.api.Folder;
  import org.apache.chemistry.opencmis.commons.enums.Action;
  import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
  import org.apache.chemistry.opencmis.commons.enums.UnfileObject;
  import org.apache.chemistry.opencmis.workbench.icons.CheckedOutIcon;
  import org.apache.chemistry.opencmis.workbench.icons.DocumentIcon;
  import org.apache.chemistry.opencmis.workbench.icons.FolderIcon;
  import org.apache.chemistry.opencmis.workbench.icons.ItemIcon;
  import org.apache.chemistry.opencmis.workbench.icons.PolicyIcon;
  import org.apache.chemistry.opencmis.workbench.icons.PwcIcon;
  import org.apache.chemistry.opencmis.workbench.icons.RelationshipIcon;
  import org.apache.chemistry.opencmis.workbench.model.ClientModel;
  import org.apache.chemistry.opencmis.workbench.model.ClientModelEvent;
  import org.apache.chemistry.opencmis.workbench.model.FolderListener;
  import org.apache.chemistry.opencmis.workbench.swing.GregorianCalendarRenderer;
  import org.apache.chemistry.opencmis.workbench.worker.DeleteWorker;
  import org.apache.chemistry.opencmis.workbench.worker.LoadFolderWorker;
  import org.apache.chemistry.opencmis.workbench.worker.LoadObjectWorker;
  import org.apache.chemistry.opencmis.workbench.worker.TempFileContentWorker;
  
  public class FolderTable extends JTable implements FolderListener {
  
      private static final long serialVersionUID = 1L;
  
      private static final String[] COLUMN_NAMES = { "", "Name", "Type", "Content Type", "Size", "Creation Date",
              "Created by", "Modification Date", "Modified by", "Id" };
      private static final int[] COLUMN_WIDTHS = { ClientHelper.OBJECT_ICON_SIZE + 4, 200, 150, 150, 80, 180, 100, 180,
              100, 300 };
      public static final int ID_COLUMN = 9;
  
      private final ClientModel model;
  
      private Map<BaseTypeId, Icon> icons;
      private Icon checkedOutIcon;
      private Icon pwcIcon;
  
      private JMenuItem downloadItem;
      private JMenuItem deleteItem;
  
      public FolderTable(final ClientModel model) {
          super();
  
          this.model = model;
  
          setModel(new FolderTableModel());
  
          setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
          setAutoResizeMode(AUTO_RESIZE_OFF);
          setAutoCreateRowSorter(true);
  
          setDefaultRenderer(GregorianCalendar.class, new GregorianCalendarRenderer());
          setTransferHandler(new FolderTransferHandler());
          setDragEnabled(true);
          setDropMode(DropMode.INSERT);
  
          for (int i = 0; i < COLUMN_WIDTHS.length; i++) {
              TableColumn column = getColumnModel().getColumn(i);
              column.setPreferredWidth(WorkbenchScale.scaleInt(COLUMN_WIDTHS[i]));
          }
  
          setRowHeight((int) (getFontMetrics(getFont()).getHeight() * 1.1));
  
          final JPopupMenu popup = new JPopupMenu();
  
          // popup menu: clipboard
          final JMenuItem clipboardAllItem = new JMenuItem("Copy all rows to clipboard");
          popup.add(clipboardAllItem);
  
          clipboardAllItem.addActionListener(new ActionListener() {
              @Override
              public void actionPerformed(ActionEvent e) {
                  ClientHelper.copyTableToClipboard(FolderTable.this, false);
              }
          });
  
          final JMenuItem clipboardSelectedItem = new JMenuItem("Copy selected rows to clipboard");
          popup.add(clipboardSelectedItem);
  
          clipboardSelectedItem.addActionListener(new ActionListener() {
              @Override
              public void actionPerformed(ActionEvent e) {
                  ClientHelper.copyTableToClipboard(FolderTable.this, true);
              }
          });
  
          popup.addSeparator();
  
          // popup menu: delete
          deleteItem = new JMenuItem("Delete");
          deleteItem.setEnabled(false);
          popup.add(deleteItem);
  
          deleteItem.addActionListener(new ActionListener() {
              @Override
              public void actionPerformed(ActionEvent e) {
                  if (model.getCurrentObject() != null) {
                      int answer = JOptionPane.showConfirmDialog(FolderTable.this,
                              "Do you really want to delete '" + model.getCurrentObject().getName() + "'?", "Delete",
                              JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
  
                      if (answer == JOptionPane.YES_OPTION) {
                          DeleteWorker worker = null;
                          if (model.getCurrentObject() instanceof Folder) {
                              worker = new DeleteWorker(FolderTable.this, (Folder) model.getCurrentObject(), true,
                                      UnfileObject.DELETE, true) {
                                  @Override
                                  protected void done() {
                                      super.done();
                                      LoadFolderWorker.reloadFolder(FolderTable.this, model);
                                  }
                              };
                          } else {
                              worker = new DeleteWorker(FolderTable.this, model.getCurrentObject()) {
                                  @Override
                                  protected void done() {
                                      super.done();
                                      LoadFolderWorker.reloadFolder(FolderTable.this, model);
                                  }
                              };
                          }
                          worker.executeTask();
                      }
                  }
              }
          });
  
          // popup menu: download
          downloadItem = new JMenuItem("Download");
          downloadItem.setEnabled(false);
          popup.add(downloadItem);
  
          downloadItem.addActionListener(new ActionListener() {
              @Override
              public void actionPerformed(ActionEvent e) {
                  if (model.getCurrentObject() != null) {
                      ClientHelper.download(FolderTable.this, model.getCurrentObject(), null);
                  }
              }
          });
  
          getSelectionModel().addListSelectionListener(new ListSelectionListener() {
              @Override
              public void valueChanged(ListSelectionEvent e) {
                  if (e.getValueIsAdjusting()) {
                      return;
                  }
  
                  int row = getSelectedRow();
                  if (row > -1) {
                      String id = getModel().getValueAt(getRowSorter().convertRowIndexToModel(row), ID_COLUMN).toString();
  
                      LoadObjectWorker worker = new LoadObjectWorker(FolderTable.this, model, id) {
                          @Override
                          protected void done() {
                              super.done();
  
                              if (model.getCurrentObject() != null
                                      && model.getCurrentObject().getAllowableActions() != null) {
                                  CmisObject object = model.getCurrentObject();
                                  deleteItem.setEnabled(object.hasAllowableAction(Action.CAN_DELETE_OBJECT)
                                          || object.hasAllowableAction(Action.CAN_DELETE_TREE));
                                  downloadItem.setEnabled(object.hasAllowableAction(Action.CAN_GET_CONTENT_STREAM));
                              } else {
                                  deleteItem.setEnabled(false);
                                  downloadItem.setEnabled(false);
                              }
                          }
                      };
                      worker.executeTask();
                  }
              }
          });
  
          addMouseListener(new MouseAdapter() {
              @Override
              public void mouseClicked(MouseEvent e) {
                  if (e.getClickCount() == 2) {
                      doAction(e.isShiftDown());
                  }
              }
  
              @Override
              public void mousePressed(MouseEvent e) {
                  maybeShowPopup(e);
              }
  
              @Override
              public void mouseReleased(MouseEvent e) {
                  maybeShowPopup(e);
              }
  
              private void maybeShowPopup(MouseEvent e) {
                  if (e.isPopupTrigger()) {
                      popup.show(e.getComponent(), e.getX(), e.getY());
                  }
              }
          });
  
          addKeyListener(new KeyListener() {
              @Override
              public void keyTyped(KeyEvent e) {
              }
  
              @Override
              public void keyReleased(KeyEvent e) {
                  if (e.getKeyCode() == KeyEvent.VK_SPACE) {
                      doAction(e.isShiftDown());
                  }
              }
  
              @Override
              public void keyPressed(KeyEvent e) {
              }
          });
  
          // load icon and set icon column size
          loadIcons();
          getColumnModel().getColumn(0)
                  .setPreferredWidth((int) (icons.get(BaseTypeId.CMIS_DOCUMENT).getIconWidth() * 1.1));
      }
  
      private void loadIcons() {
          icons = new EnumMap<BaseTypeId, Icon>(BaseTypeId.class);
          icons.put(BaseTypeId.CMIS_DOCUMENT,
                  new DocumentIcon(ClientHelper.OBJECT_ICON_SIZE, ClientHelper.OBJECT_ICON_SIZE));
          icons.put(BaseTypeId.CMIS_FOLDER, new FolderIcon(ClientHelper.OBJECT_ICON_SIZE, ClientHelper.OBJECT_ICON_SIZE));
          icons.put(BaseTypeId.CMIS_RELATIONSHIP,
                  new RelationshipIcon(ClientHelper.OBJECT_ICON_SIZE, ClientHelper.OBJECT_ICON_SIZE));
          icons.put(BaseTypeId.CMIS_POLICY, new PolicyIcon(ClientHelper.OBJECT_ICON_SIZE, ClientHelper.OBJECT_ICON_SIZE));
          icons.put(BaseTypeId.CMIS_ITEM, new ItemIcon(ClientHelper.OBJECT_ICON_SIZE, ClientHelper.OBJECT_ICON_SIZE));
  
          checkedOutIcon = new CheckedOutIcon(ClientHelper.OBJECT_ICON_SIZE, ClientHelper.OBJECT_ICON_SIZE);
          pwcIcon = new PwcIcon(ClientHelper.OBJECT_ICON_SIZE, ClientHelper.OBJECT_ICON_SIZE);
      }
  
      @Override
      public void folderLoaded(final ClientModelEvent event) {
          SwingUtilities.invokeLater(new Runnable() {
              @Override
              public void run() {
                  event.getClientModel().getCurrentChildren();
                  ((FolderTableModel) getModel()).fireTableDataChanged();
              }
          });
      }
  
      private void doAction(boolean alternate) {
          int row = getSelectedRow();
          if ((row > -1) && (row < model.getCurrentChildren().size())) {
              String id = getModel().getValueAt(getRowSorter().convertRowIndexToModel(row), ID_COLUMN).toString();
              CmisObject object = model.getFromCurrentChildren(id);
  
              if (object instanceof Document) {
                  if (alternate) {
                      ClientHelper.download(this.getParent(), object, null);
                  } else {
                      ClientHelper.open(this.getParent(), object, null);
                  }
              } else if (object instanceof Folder) {
                  LoadFolderWorker.loadFolderById(FolderTable.this, model, object.getId());
              }
          }
      }
  
      class FolderTableModel extends AbstractTableModel {
  
          private static final long serialVersionUID = 1L;
  
          @Override
          public String getColumnName(int columnIndex) {
              return COLUMN_NAMES[columnIndex];
          }
  
          @Override
          public int getColumnCount() {
              return COLUMN_NAMES.length;
          }
  
          @Override
          public int getRowCount() {
              return model.getCurrentChildren().size();
          }
  
          @Override
          public Object getValueAt(int rowIndex, int columnIndex) {
              CmisObject obj = model.getCurrentChildren().get(rowIndex);
  
              switch (columnIndex) {
              case 0:
                  if (obj instanceof Document) {
                      Document doc = (Document) obj;
                      if (Boolean.TRUE.equals(doc.isVersionSeriesCheckedOut())) {
                          if (Boolean.TRUE.equals(doc.isVersionSeriesPrivateWorkingCopy())) {
                              return pwcIcon;
                          } else {
                              return checkedOutIcon;
                          }
                      } else {
                          return icons.get(BaseTypeId.CMIS_DOCUMENT);
                      }
                  }
                  return icons.get(obj.getBaseTypeId());
              case 1:
                  return obj.getName();
              case 2:
                  return obj.getType().getId();
              case 3:
                  if (obj instanceof Document) {
                      return ((Document) obj).getContentStreamMimeType();
                  } else {
                      return null;
                  }
              case 4:
                  if (obj instanceof Document) {
                      return ((Document) obj).getContentStreamLength();
                  } else {
                      return null;
                  }
              case 5:
                  return obj.getCreationDate();
              case 6:
                  return obj.getCreatedBy();
              case 7:
                  return obj.getLastModificationDate();
              case 8:
                  return obj.getLastModifiedBy();
              case ID_COLUMN:
                  return obj.getId();
              default:
              }
  
              return "";
          }
  
          @Override
          public Class<?> getColumnClass(int columnIndex) {
              switch (columnIndex) {
              case 0:
                  return ImageIcon.class;
              case 4:
                  return Long.class;
              case 5:
              case 7:
                  return GregorianCalendar.class;
              default:
              }
  
              return String.class;
          }
      }
  
      private class FolderTransferHandler extends TransferHandler {
  
          private static final long serialVersionUID = 1L;
  
          public FolderTransferHandler() {
              super();
          }
  
          @Override
          public boolean canImport(TransferSupport support) {
              if (!support.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
                  return false;
              }
  
              if (!support.isDrop()) {
                  return false;
              }
  
              return true;
          }
  
          @SuppressWarnings("unchecked")
          @Override
          public boolean importData(TransferSupport support) {
              if (!canImport(support)) {
                  return false;
              }
  
              File file = null;
              try {
                  List<File> fileList = (List<File>) support.getTransferable()
                          .getTransferData(DataFlavor.javaFileListFlavor);
  
                  if (fileList == null || fileList.size() != 1 || fileList.get(0) == null || !fileList.get(0).isFile()) {
                      return false;
                  }
  
                  file = fileList.get(0);
              } catch (Exception ex) {
                  ClientHelper.showError(null, ex);
                  return false;
              }
  
              new CreateDocumentDialog(null, model, file);
  
              return true;
          }
  
          @Override
          public int getSourceActions(JComponent c) {
              return COPY;
          }
  
          @Override
          protected Transferable createTransferable(JComponent c) {
              int row = getSelectedRow();
              if (row < 0 || row >= model.getCurrentChildren().size()) {
                  return null;
              }
  
              String id = getValueAt(row, ID_COLUMN).toString();
              final CmisObject object = model.getFromCurrentChildren(id);
              if (!(object instanceof Document)) {
                  return null;
              }
  
              return new Transferable() {
                  @Override
                  public boolean isDataFlavorSupported(DataFlavor flavor) {
                      return flavor == DataFlavor.javaFileListFlavor;
                  }
  
                  @Override
                  public DataFlavor[] getTransferDataFlavors() {
                      return new DataFlavor[] { DataFlavor.javaFileListFlavor };
                  }
  
                  @Override
                  public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
                      if (flavor != DataFlavor.javaFileListFlavor) {
                          throw new UnsupportedFlavorException(flavor);
                      }
  
                      try {
                          TempFileContentWorker worker = new TempFileContentWorker(null, (Document) object);
                          return Collections.singletonList(worker.executeSync());
                      } catch (Exception e) {
                          ClientHelper.showError(FolderTable.this, e);
                          return null;
                      }
                  }
              };
          }
      }
  }
  