/*! * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved. */ package org.pentaho.reporting.designer.extensions.pentaho.repository.dialogs; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.commons.vfs2.FileName; import org.apache.commons.vfs2.FileObject; import org.apache.commons.vfs2.FileSystemException; import org.apache.commons.vfs2.FileType; import org.apache.commons.vfs2.VFS; import org.pentaho.reporting.designer.core.auth.AuthenticationData; import org.pentaho.reporting.designer.core.util.exceptions.UncaughtExceptionsModel; import org.pentaho.reporting.designer.extensions.pentaho.repository.Messages; import org.pentaho.reporting.designer.extensions.pentaho.repository.util.PublishUtil; import org.pentaho.reporting.libraries.base.util.DebugLog; import org.pentaho.reporting.libraries.base.util.IOUtils; import org.pentaho.reporting.libraries.base.util.StringUtils; import org.pentaho.reporting.libraries.designtime.swing.BorderlessButton; import org.pentaho.reporting.libraries.designtime.swing.CommonDialog; import org.pentaho.reporting.libraries.designtime.swing.event.DocumentChangeHandler; import org.pentaho.reporting.libraries.pensol.WebSolutionFileObject; import org.pentaho.reporting.libraries.pensol.vfs.WebSolutionFileSystem; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ComboBoxModel; import javax.swing.DefaultComboBoxModel; import javax.swing.DefaultListCellRenderer; import javax.swing.ImageIcon; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.border.EmptyBorder; import javax.swing.event.DocumentEvent; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dialog; import java.awt.Frame; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; 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.IOException; import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLDecoder; import java.util.ArrayList; public class RepositoryOpenDialog extends CommonDialog { protected class LevelUpAction extends AbstractAction { /** * Defines an <code>Action</code> object with a default description string and default icon. */ public LevelUpAction() { putValue( Action.SHORT_DESCRIPTION, Messages.getInstance().getString( "RepositoryPublishDialog.LevelUpAction.Description" ) ); final URL location = RepositoryOpenDialog.class .getResource( "/org/pentaho/reporting/designer/extensions/pentaho/repository/resources/upOneFolder.png" ); // NON-NLS if ( location != null ) { putValue( Action.SMALL_ICON, new ImageIcon( location ) ); } else { putValue( Action.NAME, Messages.getInstance().getString( "RepositoryPublishDialog.LevelUpAction.Name" ) ); } } /** * Invoked when an action occurs. */ public void actionPerformed( final ActionEvent e ) { if ( fileSystemRoot == null ) { return; } if ( fileSystemRoot.equals( selectedView ) ) { return; } try { setSelectedView( selectedView.getParent() ); } catch ( FileSystemException e1 ) { UncaughtExceptionsModel.getInstance().addException( e1 ); } } } protected class BrowseRepositoryAction extends AbstractAction { /** * Defines an <code>Action</code> object with a default description string and default icon. */ public BrowseRepositoryAction() { putValue( Action.SHORT_DESCRIPTION, Messages.getInstance().getString( "RepositoryPublishDialog.BrowseRepositoryAction.Description" ) ); final URL location = RepositoryOpenDialog.class .getResource( "/org/pentaho/reporting/designer/extensions/pentaho/repository/resources/exploreSolution.png" ); // NON-NLS if ( location != null ) { putValue( Action.SMALL_ICON, new ImageIcon( location ) ); } else { putValue( Action.NAME, Messages.getInstance().getString( "RepositoryPublishDialog.BrowseRepositoryAction.Name" ) ); } } /** * Invoked when an action occurs. */ public void actionPerformed( final ActionEvent e ) { if ( fileSystemRoot == null ) { return; } final RepositoryTreeDialog treeDialog = new RepositoryTreeDialog( RepositoryOpenDialog.this, isCreateFolderAllowed() ); try { final FileObject newLocation = treeDialog.performSelectLocation( fileSystemRoot, getFilters(), selectedView ); if ( newLocation != null ) { setSelectedView( newLocation ); } if ( treeDialog.isDirty() ) { table.refresh(); } } catch ( FileSystemException e1 ) { UncaughtExceptionsModel.getInstance().addException( e1 ); } } } private class TableInputHandler extends MouseAdapter implements KeyListener, ListSelectionListener { private TableInputHandler() { } /** * Invoked when a key has been typed. See the class description for {@link java.awt.event.KeyEvent} for a definition * of a key typed event. */ public void keyTyped( final KeyEvent e ) { } /** * Invoked when a key has been pressed. See the class description for {@link java.awt.event.KeyEvent} for a * definition of a key pressed event. */ public void keyPressed( final KeyEvent e ) { if ( e.getKeyCode() == KeyEvent.VK_ENTER ) { final int selectedRowRaw = table.getSelectedRow(); if ( selectedRowRaw == -1 ) { return; } final int selectedRow = table.convertRowIndexToModel( selectedRowRaw ); final FileObject selectedFileObject = table.getSelectedFileObject( selectedRow ); if ( selectedFileObject == null ) { return; } try { if ( FileType.FOLDER.equals( selectedFileObject.getType() ) ) { setSelectedView( selectedFileObject ); } } catch ( FileSystemException e1 ) { // ignore .. } } } /** * Invoked when a key has been released. See the class description for {@link java.awt.event.KeyEvent} for a * definition of a key released event. */ public void keyReleased( final KeyEvent e ) { } /** * {@inheritDoc} */ public void mouseClicked( final MouseEvent e ) { if ( e.getButton() != 1 ) { return; } if ( e.getClickCount() < 2 ) { return; } final int selectedRowRaw = table.getSelectedRow(); if ( selectedRowRaw == -1 ) { return; } final int selectedRow = table.convertRowIndexToModel( selectedRowRaw ); final FileObject selectedFileObject = table.getSelectedFileObject( selectedRow ); if ( selectedFileObject == null ) { return; } try { if ( FileType.FOLDER.equals( selectedFileObject.getType() ) ) { setSelectedView( selectedFileObject ); } else if ( FileType.FILE.equals( selectedFileObject.getType() ) ) { if ( isDoubleClickConfirmsDialog() == false ) { return; } setConfirmed( true ); setVisible( false ); } } catch ( FileSystemException e1 ) { // ignore .. } } /** * Called whenever the value of the selection changes. * * @param e * the event that characterizes the change. */ public void valueChanged( final ListSelectionEvent e ) { final int selectedRowRaw = table.getSelectedRow(); if ( selectedRowRaw == -1 ) { return; } final int selectedRow = table.convertRowIndexToModel( selectedRowRaw ); final FileObject selectedFileObject = table.getSelectedFileObject( selectedRow ); if ( selectedFileObject == null ) { return; } try { if ( selectedFileObject.getType() == FileType.FILE ) { fileNameTextField.setText( URLDecoder.decode( selectedFileObject.getName().getBaseName().replaceAll( "\\+", "%2B" ), "UTF-8" ) ); } } catch ( FileSystemException e1 ) { // ignore .. } catch ( UnsupportedEncodingException e1 ) { // ignore .. } } } private static class FileObjectRenderer extends DefaultListCellRenderer { private FileObjectRenderer() { } public Component getListCellRendererComponent( final JList list, final Object value, final int index, final boolean isSelected, final boolean cellHasFocus ) { if ( value instanceof WebSolutionFileObject ) { final WebSolutionFileObject fo = (WebSolutionFileObject) value; final FileName fileName = fo.getName(); try { return super.getListCellRendererComponent( list, fileName.getPathDecoded(), index, isSelected, cellHasFocus ); } catch ( FileSystemException e ) { // ignored .. } } return super.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus ); } } private class FileNameValidator extends DocumentChangeHandler { private FileNameValidator() { } protected void handleChange( final DocumentEvent e ) { getConfirmAction().setEnabled( validateInputs( false ) ); } } private class SelectLocationAction implements ActionListener { private SelectLocationAction() { } /** * Invoked when an action occurs. */ public void actionPerformed( final ActionEvent e ) { if ( locationCombo.getSelectedItem() instanceof FileObject ) { final FileObject selectedItem = (FileObject) locationCombo.getSelectedItem(); if ( selectedItem.equals( getSelectedView() ) == false ) { setSelectedView( selectedItem ); } } } } private class ShowHiddenFilesAction extends AbstractAction { private ShowHiddenFilesAction() { putValue( Action.NAME, Messages.getInstance().getString( "ShowHiddenFilesAction.Name" ) ); setSelected( Boolean.FALSE ); } private Boolean getSelected() { return (Boolean) this.getValue( Action.SELECTED_KEY ); } private void setSelected( final Boolean selected ) { this.putValue( Action.SELECTED_KEY, selected ); } /** * Invoked when an action occurs. */ public void actionPerformed( final ActionEvent e ) { table.setShowHiddenFiles( Boolean.TRUE.equals( getSelected() ) ); } } private static final String[] REPORT_FILTER = new String[] { ".prpt", ".report", ".prpti" }; private static final Log logger = LogFactory.getLog( RepositoryOpenDialog.class ); private RepositoryTable table; private JTextField fileNameTextField; private JComboBox locationCombo; private FileObject fileSystemRoot; private FileObject selectedView; public RepositoryOpenDialog() { init(); } public RepositoryOpenDialog( final Frame owner ) { super( owner ); init(); } public RepositoryOpenDialog( final Dialog owner ) { super( owner ); init(); } public void init() { locationCombo = new JComboBox(); locationCombo.setRenderer( new FileObjectRenderer() ); locationCombo.addActionListener( new SelectLocationAction() ); fileNameTextField = new JTextField(); fileNameTextField.getDocument().addDocumentListener( new FileNameValidator() ); table = new RepositoryTable(); table.setFilters( REPORT_FILTER ); table.addKeyListener( new TableInputHandler() ); table.addMouseListener( new TableInputHandler() ); table.getSelectionModel().addListSelectionListener( new TableInputHandler() ); super.init(); } protected String getDialogId() { return "ReportDesigner.Pentaho.RepositoryOpen"; } public String[] getFilters() { return table.getFilters(); } public void setFilters( final String[] filters ) { table.setFilters( filters ); } protected FileObject getSelectedView() { return selectedView; } public void setSelectedView( final FileObject selectedView ) { this.selectedView = selectedView; if ( selectedView != null ) { logger.debug( "Setting selected view to " + selectedView ); try { if ( selectedView.getType() == FileType.FILE ) { logger.debug( "Setting filename in selected view to " + selectedView.getName().getBaseName() ); this.fileNameTextField.setText( URLDecoder.decode( selectedView.getName().getBaseName(), "UTF-8" ) ); } } catch ( Exception e ) { // can be ignored .. logger.debug( "Unable to determine file type. This is not fatal.", e ); } final ComboBoxModel comboBoxModel = createLocationModel( selectedView ); this.locationCombo.setModel( comboBoxModel ); this.table.setSelectedPath( (FileObject) comboBoxModel.getSelectedItem() ); } else { this.fileNameTextField.setText( null ); this.table.setSelectedPath( null ); this.locationCombo.setModel( new DefaultComboBoxModel() ); } } private ComboBoxModel createLocationModel( final FileObject selectedFolder ) { if ( fileSystemRoot == null ) { return new DefaultComboBoxModel(); } try { final ArrayList<FileObject> list = new ArrayList<FileObject>(); FileObject folder = selectedFolder; while ( folder != null ) { if ( fileSystemRoot.equals( folder ) ) { break; } if ( folder.getType() != FileType.FILE ) { list.add( folder ); } final FileObject parent = folder.getParent(); if ( folder.equals( parent ) ) { // protect yourself against infinite loops .. break; } folder = parent; } list.add( fileSystemRoot ); final DefaultComboBoxModel model = new DefaultComboBoxModel( list.toArray() ); model.setSelectedItem( list.get( 0 ) ); return model; } catch ( FileSystemException e ) { return new DefaultComboBoxModel(); } } public String performOpen( final AuthenticationData loginData, final String previousSelection ) throws FileSystemException, UnsupportedEncodingException { fileSystemRoot = PublishUtil.createVFSConnection( VFS.getManager(), loginData ); if ( previousSelection == null ) { setSelectedView( fileSystemRoot ); } else { final FileObject view = fileSystemRoot.resolveFile( previousSelection ); if ( view == null ) { setSelectedView( fileSystemRoot ); } else { if ( view.exists() == false ) { setSelectedView( fileSystemRoot ); } else if ( view.getType() == FileType.FOLDER ) { setSelectedView( view ); } else { setSelectedView( view.getParent() ); } } } if ( StringUtils.isEmpty( fileNameTextField.getText(), true ) && previousSelection != null ) { final String fileName = IOUtils.getInstance().getFileName( previousSelection ); DebugLog.log( "Setting filename to " + fileName ); fileNameTextField.setText( fileName ); } getConfirmAction().setEnabled( validateInputs( false ) ); if ( super.performEdit() == false || selectedView == null ) { return null; } return getSelectedFile(); } protected String getSelectedFile() throws FileSystemException, UnsupportedEncodingException { if ( StringUtils.isEmpty( fileNameTextField.getText() ) ) { return null; } if ( selectedView.getType() == FileType.FILE ) { selectedView = selectedView.getParent(); } final FileObject targetFile = selectedView.resolveFile( fileNameTextField.getText().replaceAll( "\\%", "%25" ).replaceAll( "\\!", "%21" ) .replaceAll( ":", "%3A" ) ); return targetFile.getName().getPathDecoded(); } protected RepositoryTable getTable() { return table; } protected JTextField getFileNameTextField() { return fileNameTextField; } protected JComboBox getLocationCombo() { return locationCombo; } protected Component createContentPane() { final JPanel centerCarrier = new JPanel(); centerCarrier.setLayout( new BorderLayout() ); centerCarrier.setBorder( new EmptyBorder( 5, 5, 5, 5 ) ); centerCarrier.add( new JScrollPane( table ), BorderLayout.CENTER ); centerCarrier.add( new JCheckBox( new ShowHiddenFilesAction() ), BorderLayout.SOUTH ); final JPanel contentPanel = new JPanel(); contentPanel.setLayout( new BorderLayout() ); contentPanel.add( centerCarrier, BorderLayout.CENTER ); contentPanel.add( createHeaderPanel(), BorderLayout.NORTH ); return contentPanel; } protected boolean isCreateFolderAllowed() { return false; } protected boolean isDoubleClickConfirmsDialog() { return true; } protected JPanel createHeaderPanel() { final JPanel publishHeaderPanel = new JPanel( new GridBagLayout() ); final GridBagConstraints c = new GridBagConstraints(); c.gridx = 0; c.gridy = 0; c.fill = GridBagConstraints.HORIZONTAL; c.insets = new Insets( 5, 5, 5, 5 ); publishHeaderPanel.add( new JLabel( Messages.getInstance().getString( "RepositoryPublishDialog.ReportName" ) ), c ); c.gridy = 1; c.insets = new Insets( 2, 5, 0, 5 ); publishHeaderPanel.add( fileNameTextField, c ); c.gridy = 4; c.fill = GridBagConstraints.HORIZONTAL; c.insets = new Insets( 2, 5, 0, 5 ); publishHeaderPanel.add( new JLabel( Messages.getInstance().getString( "RepositoryPublishDialog.Location" ) ), c ); c.insets = new Insets( 0, 0, 0, 0 ); c.gridx = 0; c.gridy = 5; c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 1.0; c.anchor = GridBagConstraints.WEST; publishHeaderPanel.add( createLocationFieldPanel(), c ); return publishHeaderPanel; } protected JPanel createLocationFieldPanel() { final GridBagConstraints c = new GridBagConstraints(); final JPanel locationFieldPanel = new JPanel(); locationFieldPanel.setLayout( new GridBagLayout() ); c.insets = new Insets( 0, 5, 5, 0 ); c.gridx = 0; c.gridy = 0; c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 1.0; c.anchor = GridBagConstraints.WEST; locationFieldPanel.add( locationCombo, c ); c.insets = new Insets( 5, 8, 5, 0 ); c.gridx = 1; c.gridy = 0; c.fill = GridBagConstraints.NONE; c.weightx = 0.0; c.anchor = GridBagConstraints.WEST; locationFieldPanel.add( new BorderlessButton( new LevelUpAction() ), c ); c.gridx = 2; c.gridy = 0; c.fill = GridBagConstraints.NONE; c.weightx = 0.0; c.anchor = GridBagConstraints.EAST; locationFieldPanel.add( new BorderlessButton( new BrowseRepositoryAction() ), c ); return locationFieldPanel; } protected boolean validateInputs( final boolean onConfirm ) { if ( StringUtils.isEmpty( fileNameTextField.getText() ) ) { return false; } return true; } public void refresh() throws IOException { final WebSolutionFileSystem fileSystem = (WebSolutionFileSystem) this.fileSystemRoot.getFileSystem(); fileSystem.getLocalFileModel().refresh(); table.refresh(); } }