api/src/org/netbeans/modules/jackpot30/impl/indexing/options/CustomizeRemoteIndex.java
author Jan Lahoda <jlahoda@netbeans.org>
Thu, 28 Apr 2011 20:13:40 +0200
changeset 572 d9b17eac9870
parent 552 e0855a9f6040
permissions -rw-r--r--
Preventing various exception in the RemoteIndices dialog
     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2010-2011 Sun Microsystems, Inc. All rights reserved.
     5  *
     6  * The contents of this file are subject to the terms of either the GNU
     7  * General Public License Version 2 only ("GPL") or the Common
     8  * Development and Distribution License("CDDL") (collectively, the
     9  * "License"). You may not use this file except in compliance with the
    10  * License. You can obtain a copy of the License at
    11  * http://www.netbeans.org/cddl-gplv2.html
    12  * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    13  * specific language governing permissions and limitations under the
    14  * License.  When distributing the software, include this License Header
    15  * Notice in each file and include the License file at
    16  * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
    17  * particular file as subject to the "Classpath" exception as provided
    18  * by Sun in the GPL Version 2 section of the License file that
    19  * accompanied this code. If applicable, add the following below the
    20  * License Header, with the fields enclosed by brackets [] replaced by
    21  * your own identifying information:
    22  * "Portions Copyrighted [year] [name of copyright owner]"
    23  *
    24  * If you wish your version of this file to be governed by only the CDDL
    25  * or only the GPL Version 2, indicate your decision by adding
    26  * "[Contributor] elects to include this software in this distribution
    27  * under the [CDDL or GPL Version 2] license." If you do not indicate a
    28  * single choice of license, a recipient has the option to distribute
    29  * your version of this file under either the CDDL, the GPL Version 2 or
    30  * to extend the choice of license to its licensees as provided above.
    31  * However, if you add GPL Version 2 code and therefore, elected the GPL
    32  * Version 2 license, then the option applies only if the new code is
    33  * made subject to such option by the copyright holder.
    34  *
    35  * Contributor(s):
    36  *
    37  * Portions Copyrighted 2010-2011 Sun Microsystems, Inc.
    38  */
    39 package org.netbeans.modules.jackpot30.impl.indexing.options;
    40 
    41 import java.awt.Component;
    42 import java.io.File;
    43 import java.net.MalformedURLException;
    44 import java.net.URISyntaxException;
    45 import java.net.URL;
    46 import java.text.DateFormat;
    47 import java.util.Collection;
    48 import java.util.Date;
    49 import java.util.HashMap;
    50 import java.util.Map;
    51 import java.util.concurrent.atomic.AtomicBoolean;
    52 import java.util.concurrent.atomic.AtomicReference;
    53 import java.util.logging.Level;
    54 import java.util.logging.Logger;
    55 import javax.swing.DefaultComboBoxModel;
    56 import javax.swing.DefaultListCellRenderer;
    57 import javax.swing.JButton;
    58 import javax.swing.JFileChooser;
    59 import javax.swing.JList;
    60 import javax.swing.JTextField;
    61 import javax.swing.SwingUtilities;
    62 import javax.swing.UIManager;
    63 import javax.swing.event.DocumentEvent;
    64 import javax.swing.event.DocumentListener;
    65 import org.codeviation.pojson.Pojson;
    66 import org.netbeans.modules.jackpot30.impl.WebUtilities;
    67 import org.netbeans.modules.jackpot30.impl.indexing.IndexInfo;
    68 import org.netbeans.modules.jackpot30.impl.indexing.RemoteIndex;
    69 import org.openide.NotificationLineSupport;
    70 import org.openide.util.RequestProcessor;
    71 
    72 /**
    73  *
    74  * @author lahvac
    75  */
    76 public class CustomizeRemoteIndex extends javax.swing.JPanel {
    77 
    78     private final JButton okButton;
    79 
    80     public CustomizeRemoteIndex(JButton okButton) {
    81         this.okButton = okButton;
    82         initComponents();
    83         DocumentListener updateErrorsListener = new DocumentListener() {
    84             public void insertUpdate(DocumentEvent e) {
    85                 updateErrors();
    86             }
    87             public void removeUpdate(DocumentEvent e) {
    88                 updateErrors();
    89             }
    90             public void changedUpdate(DocumentEvent e) {}
    91         };
    92         folder.getDocument().addDocumentListener(updateErrorsListener);
    93         indexURL.getDocument().addDocumentListener(new DocumentListener() {
    94             public void insertUpdate(DocumentEvent e) {
    95                 indexURLUpdated();
    96             }
    97             public void removeUpdate(DocumentEvent e) {
    98                 indexURLUpdated();
    99             }
   100             public void changedUpdate(DocumentEvent e) {
   101             }
   102         });
   103         indexInfo.setFont(UIManager.getFont("Label.font"));
   104         indexInfo.setBackground(UIManager.getColor("Label.background"));
   105         indexInfo.setDisabledTextColor(UIManager.getColor("Label.foreground"));
   106     }
   107 
   108     /** This method is called from within the constructor to
   109      * initialize the form.
   110      * WARNING: Do NOT modify this code. The content of this method is
   111      * always regenerated by the Form Editor.
   112      */
   113     @SuppressWarnings("unchecked")
   114     // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
   115     private void initComponents() {
   116         java.awt.GridBagConstraints gridBagConstraints;
   117 
   118         buttonGroup1 = new javax.swing.ButtonGroup();
   119         folderPanel = new javax.swing.JPanel();
   120         folderLabel = new javax.swing.JLabel();
   121         folderChooser = new javax.swing.JButton();
   122         folder = new javax.swing.JTextField();
   123         indexInfo = new javax.swing.JTextArea();
   124         remoteIndexPanel = new javax.swing.JPanel();
   125         indexURL = new javax.swing.JTextField();
   126         indexURLLabel = new javax.swing.JLabel();
   127         subIndex = new javax.swing.JComboBox();
   128         jLabel1 = new javax.swing.JLabel();
   129 
   130         setBorder(javax.swing.BorderFactory.createEmptyBorder(12, 12, 12, 12));
   131         setLayout(new java.awt.GridBagLayout());
   132 
   133         folderPanel.setLayout(new java.awt.GridBagLayout());
   134 
   135         org.openide.awt.Mnemonics.setLocalizedText(folderLabel, org.openide.util.NbBundle.getMessage(CustomizeRemoteIndex.class, "CustomizeRemoteIndex.folderLabel.text")); // NOI18N
   136         gridBagConstraints = new java.awt.GridBagConstraints();
   137         gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
   138         gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 6);
   139         folderPanel.add(folderLabel, gridBagConstraints);
   140 
   141         org.openide.awt.Mnemonics.setLocalizedText(folderChooser, org.openide.util.NbBundle.getMessage(CustomizeRemoteIndex.class, "CustomizeRemoteIndex.folderChooser.text")); // NOI18N
   142         folderChooser.addActionListener(new java.awt.event.ActionListener() {
   143             public void actionPerformed(java.awt.event.ActionEvent evt) {
   144                 folderChooserActionPerformed(evt);
   145             }
   146         });
   147         gridBagConstraints = new java.awt.GridBagConstraints();
   148         gridBagConstraints.gridx = 2;
   149         gridBagConstraints.gridy = 0;
   150         gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
   151         gridBagConstraints.insets = new java.awt.Insets(0, 6, 0, 0);
   152         folderPanel.add(folderChooser, gridBagConstraints);
   153 
   154         folder.setText(org.openide.util.NbBundle.getMessage(CustomizeRemoteIndex.class, "CustomizeRemoteIndex.folder.text")); // NOI18N
   155         gridBagConstraints = new java.awt.GridBagConstraints();
   156         gridBagConstraints.gridx = 1;
   157         gridBagConstraints.gridy = 0;
   158         gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
   159         gridBagConstraints.weightx = 1.0;
   160         folderPanel.add(folder, gridBagConstraints);
   161 
   162         gridBagConstraints = new java.awt.GridBagConstraints();
   163         gridBagConstraints.gridx = 0;
   164         gridBagConstraints.gridy = 0;
   165         gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
   166         gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
   167         gridBagConstraints.weightx = 1.0;
   168         add(folderPanel, gridBagConstraints);
   169 
   170         indexInfo.setColumns(20);
   171         indexInfo.setEditable(false);
   172         indexInfo.setRows(5);
   173         indexInfo.setBorder(null);
   174         gridBagConstraints = new java.awt.GridBagConstraints();
   175         gridBagConstraints.gridx = 0;
   176         gridBagConstraints.gridy = 2;
   177         gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
   178         gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
   179         gridBagConstraints.weightx = 1.0;
   180         gridBagConstraints.weighty = 1.0;
   181         gridBagConstraints.insets = new java.awt.Insets(6, 0, 0, 0);
   182         add(indexInfo, gridBagConstraints);
   183 
   184         indexURL.setColumns(40);
   185         indexURL.setText(org.openide.util.NbBundle.getMessage(CustomizeRemoteIndex.class, "CustomizeRemoteIndex.indexURL.text")); // NOI18N
   186 
   187         org.openide.awt.Mnemonics.setLocalizedText(indexURLLabel, org.openide.util.NbBundle.getMessage(CustomizeRemoteIndex.class, "CustomizeRemoteIndex.indexURLLabel.text")); // NOI18N
   188 
   189         subIndex.addActionListener(new java.awt.event.ActionListener() {
   190             public void actionPerformed(java.awt.event.ActionEvent evt) {
   191                 subIndexActionPerformed(evt);
   192             }
   193         });
   194 
   195         org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(CustomizeRemoteIndex.class, "CustomizeRemoteIndex.jLabel1.text")); // NOI18N
   196 
   197         javax.swing.GroupLayout remoteIndexPanelLayout = new javax.swing.GroupLayout(remoteIndexPanel);
   198         remoteIndexPanel.setLayout(remoteIndexPanelLayout);
   199         remoteIndexPanelLayout.setHorizontalGroup(
   200             remoteIndexPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
   201             .addGroup(remoteIndexPanelLayout.createSequentialGroup()
   202                 .addContainerGap()
   203                 .addGroup(remoteIndexPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
   204                     .addGroup(remoteIndexPanelLayout.createSequentialGroup()
   205                         .addGap(12, 12, 12)
   206                         .addComponent(jLabel1)
   207                         .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
   208                         .addComponent(subIndex, 0, 589, Short.MAX_VALUE))
   209                     .addGroup(remoteIndexPanelLayout.createSequentialGroup()
   210                         .addComponent(indexURLLabel)
   211                         .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
   212                         .addComponent(indexURL, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
   213                 .addContainerGap())
   214         );
   215         remoteIndexPanelLayout.setVerticalGroup(
   216             remoteIndexPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
   217             .addGroup(remoteIndexPanelLayout.createSequentialGroup()
   218                 .addContainerGap()
   219                 .addGroup(remoteIndexPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
   220                     .addComponent(indexURLLabel)
   221                     .addComponent(indexURL, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
   222                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
   223                 .addGroup(remoteIndexPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
   224                     .addComponent(jLabel1)
   225                     .addComponent(subIndex, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
   226                 .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
   227         );
   228 
   229         gridBagConstraints = new java.awt.GridBagConstraints();
   230         gridBagConstraints.gridx = 0;
   231         gridBagConstraints.gridy = 1;
   232         gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
   233         gridBagConstraints.weightx = 1.0;
   234         add(remoteIndexPanel, gridBagConstraints);
   235     }// </editor-fold>//GEN-END:initComponents
   236 
   237     private void folderChooserActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_folderChooserActionPerformed
   238         showFileChooser(folder);
   239 }//GEN-LAST:event_folderChooserActionPerformed
   240 
   241     private void subIndexActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_subIndexActionPerformed
   242         subindexSelectionUpdated();
   243     }//GEN-LAST:event_subIndexActionPerformed
   244 
   245     // Variables declaration - do not modify//GEN-BEGIN:variables
   246     private javax.swing.ButtonGroup buttonGroup1;
   247     private javax.swing.JTextField folder;
   248     private javax.swing.JButton folderChooser;
   249     private javax.swing.JLabel folderLabel;
   250     private javax.swing.JPanel folderPanel;
   251     private javax.swing.JTextArea indexInfo;
   252     private javax.swing.JTextField indexURL;
   253     private javax.swing.JLabel indexURLLabel;
   254     private javax.swing.JLabel jLabel1;
   255     private javax.swing.JPanel remoteIndexPanel;
   256     private javax.swing.JComboBox subIndex;
   257     // End of variables declaration//GEN-END:variables
   258 
   259     private void showFileChooser(JTextField folder) {
   260         JFileChooser c = new JFileChooser();
   261 
   262         c.setSelectedFile(new File(folder.getText()));
   263         c.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
   264         c.setMultiSelectionEnabled(false);
   265         c.setApproveButtonText("Select");
   266 
   267         if (c.showDialog(this, null) == JFileChooser.APPROVE_OPTION) {
   268             folder.setText(c.getSelectedFile().getAbsolutePath());
   269         }
   270     }
   271 
   272     public void setIndex(RemoteIndex index) {
   273         folder.setText(index.folder);
   274         indexURL.setText(index.remote.toExternalForm());
   275         subIndex.setSelectedItem(index.remoteSegment);
   276     }
   277 
   278     public RemoteIndex getIndex() {
   279         try {
   280             return RemoteIndex.create(folder.getText(), new URL(indexURL.getText()), (String) subIndex.getSelectedItem());
   281         } catch (MalformedURLException ex) {
   282             throw new IllegalStateException(ex);
   283         }
   284     }
   285 
   286     private NotificationLineSupport notificationSupport;
   287 
   288     public void setNotificationSupport(NotificationLineSupport notificationSupport) {
   289         this.notificationSupport = notificationSupport;
   290     }
   291 
   292     private void updateErrors() {
   293         notificationSupport.clearMessages();
   294         
   295         File folderFile = new File(folder.getText());
   296 
   297         if (!folderFile.exists()) {
   298             notificationSupport.setErrorMessage("Specified directory does not exist.");
   299             okButton.setEnabled(false);
   300             return;
   301         }
   302 
   303         if (!folderFile.isDirectory()) {
   304             notificationSupport.setErrorMessage("Specified directory is not directory.");
   305             okButton.setEnabled(false);
   306             return ;
   307         }
   308 
   309         if (checkingIndexURL.get()) {
   310             notificationSupport.setInformationMessage("Checking index URL");
   311             okButton.setEnabled(false);
   312             return;
   313         }
   314 
   315         String urlError = checkingIndexURLError.get();
   316 
   317         if (urlError != null) {
   318             notificationSupport.setErrorMessage(urlError);
   319             okButton.setEnabled(false);
   320             return;
   321         }
   322         
   323         okButton.setEnabled(true);
   324     }
   325 
   326     private final AtomicBoolean checkingIndexURL = new AtomicBoolean();
   327     private final AtomicReference<String> checkingIndexURLContentCopy = new AtomicReference<String>();
   328     private final AtomicReference<String> checkingIndexURLError = new AtomicReference<String>();
   329 
   330     private void indexURLUpdated() {
   331         checkingIndexURLContentCopy.set(indexURL.getText());
   332         urlCheckerTask.cancel();
   333         urlCheckerTask.schedule(50);
   334     }
   335 
   336     private static final RequestProcessor WORKER = new RequestProcessor(CustomizeRemoteIndex.class.getName(), 1, false, false);
   337     private final RequestProcessor.Task urlCheckerTask = WORKER.create(new Runnable() {
   338 
   339         public void run() {
   340             checkingIndexURL.set(true);
   341             checkingIndexURLError.set(null);
   342 
   343             SwingUtilities.invokeLater(new Runnable() {
   344                 public void run() {
   345                     updateErrors();
   346                 }
   347             });
   348 
   349             String urlText = checkingIndexURLContentCopy.get();
   350             Collection<? extends String> subindices = null;
   351 
   352             try {
   353                 URL url = new URL(urlText);
   354 
   355                 if (!url.getPath().endsWith("/"))
   356                     url = new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getPath() + "/" + (url.getQuery() != null ? "?" + url.getQuery() : ""));
   357                 
   358                 subindices = WebUtilities.requestStringArrayResponse(url.toURI().resolve("list"));
   359 
   360                 if (subindices.isEmpty()) {
   361                    checkingIndexURLError.set("Not an index.");
   362                 }
   363             } catch (URISyntaxException ex) {
   364                 checkingIndexURLError.set(ex.getLocalizedMessage());
   365             } catch (MalformedURLException ex) {
   366                 checkingIndexURLError.set(ex.getLocalizedMessage());
   367             } catch (ThreadDeath td) {
   368                 throw td;
   369             } catch (Throwable t) {//#6541019
   370                 checkingIndexURLError.set("Invalid URL");
   371             }
   372             
   373             checkingIndexURL.set(false);
   374 
   375             final Collection<? extends String> subindicesFinal = subindices;
   376 
   377             SwingUtilities.invokeLater(new Runnable() {
   378                 public void run() {
   379                     updateErrors();
   380 
   381                     if (subindicesFinal == null || subindicesFinal.isEmpty()) return;
   382 
   383                     DefaultComboBoxModel model = (DefaultComboBoxModel) subIndex.getModel();
   384                     String selected = (String) model.getSelectedItem();
   385 
   386                     model.removeAllElements();
   387 
   388                     boolean containsSelection = false;
   389                     Map<String, String> displayNames = new HashMap<String, String>();
   390 
   391                     for (String subindex : subindicesFinal) {
   392                         String[] subindexSplit = subindex.split(":", 2);
   393                         if (subindexSplit[0].equals(selected)) containsSelection = true;
   394                         model.addElement(subindexSplit[0]);
   395                         displayNames.put(subindexSplit[0], subindexSplit[1]);
   396                     }
   397 
   398                     if (containsSelection) {
   399                         model.setSelectedItem(selected);
   400                     }
   401 
   402                     subindexSelectionUpdated();
   403                     subIndex.setRenderer(new RendererImpl(displayNames));
   404                 }
   405             });
   406         }
   407     });
   408 
   409     private final AtomicReference<String> indexInfoURLContentCopy = new AtomicReference<String>();
   410     private final AtomicReference<String> indexInfoSubIndexCopy = new AtomicReference<String>();
   411     private void subindexSelectionUpdated() {
   412         indexInfoURLContentCopy.set(indexURL.getText());
   413         indexInfoSubIndexCopy.set((String) subIndex.getSelectedItem());
   414         indexInfoTask.cancel();
   415         indexInfoTask.schedule(50);
   416     }
   417 
   418     private final RequestProcessor.Task indexInfoTask = WORKER.create(new Runnable() {
   419 
   420         public void run() {
   421             String urlText = indexInfoURLContentCopy.get();
   422             String subIndex = indexInfoSubIndexCopy.get();
   423             IndexInfo info = null;
   424 
   425             try {
   426                 URL url = new URL(urlText);
   427 
   428                 if (!url.getPath().endsWith("/"))
   429                     url = new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getPath() + "/" + (url.getQuery() != null ? "?" + url.getQuery() : ""));
   430 
   431                 String indexInfoText = WebUtilities.requestStringResponse(url.toURI().resolve("info?path=" + WebUtilities.escapeForQuery(subIndex)));
   432                 info = IndexInfo.empty();
   433 
   434                 if (indexInfoText != null)
   435                     Pojson.update(info, indexInfoText);
   436             } catch (URISyntaxException ex) {
   437                 Logger.getLogger(CustomizeRemoteIndex.class.getName()).log(Level.FINE, null, ex);
   438             } catch (MalformedURLException ex) {
   439                 Logger.getLogger(CustomizeRemoteIndex.class.getName()).log(Level.FINE, null, ex);
   440             }
   441 
   442             final IndexInfo infoFinal = info;
   443 
   444             SwingUtilities.invokeLater(new Runnable() {
   445                 public void run() {
   446                     if (infoFinal != null) {
   447                         indexInfo.setText(toDisplayText(infoFinal));
   448                     } else {
   449                         indexInfo.setText("");
   450                     }
   451                 }
   452             });
   453         }
   454     });
   455 
   456     private static String toDisplayText(IndexInfo info) {
   457         StringBuilder sb = new StringBuilder();
   458 
   459         if (info.sourceLocation != null) {
   460             sb.append("Source Location: ").append(info.sourceLocation).append("\n");
   461         }
   462         if (info.lastUpdate >= 0) {
   463             sb.append("Last Update:\t").append(DateFormat.getDateTimeInstance().format(new Date(info.lastUpdate))).append("\n");
   464         }
   465         if (info.totalFiles >= 0) {
   466             sb.append("Indexed Files:\t").append(info.totalFiles).append("\n");
   467         }
   468 
   469         return sb.toString();
   470     }
   471 
   472     private static final class RendererImpl extends DefaultListCellRenderer {
   473         private final Map<String, String> displayNames;
   474         public RendererImpl(Map<String, String> displayNames) {
   475             this.displayNames = displayNames;
   476         }
   477 
   478         @Override
   479         public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
   480             return super.getListCellRendererComponent(list, displayNames.get(value), index, isSelected, cellHasFocus);
   481         }
   482 
   483     }
   484 }