| /* |
| * 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. |
| */ |
| /* |
| * Contributor(s): theanuradha@netbeans.org |
| */ |
| package org.netbeans.modules.maven.hints.ui; |
| |
| import java.awt.Image; |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import javax.swing.JButton; |
| import javax.swing.SwingUtilities; |
| import javax.swing.event.DocumentEvent; |
| import javax.swing.event.DocumentListener; |
| import org.apache.lucene.search.BooleanQuery; |
| import org.apache.maven.model.Dependency; |
| import org.netbeans.api.annotations.common.StaticResource; |
| import org.netbeans.api.project.Project; |
| import org.netbeans.modules.maven.api.NbMavenProject; |
| import static org.netbeans.modules.maven.hints.ui.Bundle.*; |
| import org.netbeans.modules.maven.hints.ui.nodes.ArtifactNode; |
| import org.netbeans.modules.maven.hints.ui.nodes.VersionNode; |
| import org.netbeans.modules.maven.indexer.api.NBVersionInfo; |
| import org.netbeans.modules.maven.indexer.api.RepositoryPreferences; |
| import org.netbeans.modules.maven.indexer.api.RepositoryQueries; |
| import org.netbeans.modules.maven.indexer.api.RepositoryQueries.Result; |
| import org.openide.explorer.ExplorerManager; |
| import org.openide.explorer.view.BeanTreeView; |
| import org.openide.nodes.AbstractNode; |
| import org.openide.nodes.Children; |
| import org.openide.nodes.FilterNode; |
| import org.openide.nodes.Node; |
| import org.openide.util.Cancellable; |
| import org.openide.util.Exceptions; |
| import org.openide.util.ImageUtilities; |
| import org.openide.util.NbBundle.Messages; |
| import org.openide.util.RequestProcessor; |
| import org.openide.util.RequestProcessor.Task; |
| import org.openide.util.lookup.AbstractLookup; |
| import org.openide.util.lookup.InstanceContent; |
| |
| /** |
| * |
| * @author Anuradha G |
| */ |
| @Messages("BTN_Add=Add") |
| public class SearchDependencyUI extends javax.swing.JPanel implements ExplorerManager.Provider { |
| private static final @StaticResource String WAIT = "org/netbeans/modules/maven/hints/wait.gif"; |
| private static final @StaticResource String EMPTY_IMAGE = "org/netbeans/modules/maven/hints/empty.png"; |
| |
| private ExplorerManager explorerManager = new ExplorerManager(); |
| private JButton addButton = new JButton(BTN_Add()); |
| private BeanTreeView beanTreeView; |
| private NBVersionInfo nbvi; |
| private Tsk task = null; |
| private static final RequestProcessor RP = new RequestProcessor(SearchDependencyUI.class.getName(),10); |
| private Project project; |
| |
| private ResultsRootNode resultsRootNode; |
| |
| /** Creates new form SearchDependencyUI */ |
| public SearchDependencyUI(String clazz, Project mavProj) { |
| initComponents(); |
| project = mavProj; |
| beanTreeView = (BeanTreeView) treeView; |
| beanTreeView.setPopupAllowed(false); |
| beanTreeView.setRootVisible(false); |
| beanTreeView.setDefaultActionAllowed(true); |
| addButton.setEnabled(false); |
| |
| txtClassName.setText(clazz); |
| txtClassName.selectAll(); |
| explorerManager.addPropertyChangeListener(new PropertyChangeListener() { |
| @Override |
| public void propertyChange(PropertyChangeEvent arg0) { |
| if (arg0.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {//NOI18N |
| |
| Node[] selectedNodes = explorerManager.getSelectedNodes(); |
| |
| for (Node node : selectedNodes) { |
| if (node instanceof VersionNode) { |
| |
| nbvi=((VersionNode) node).getNBVersionInfo(); |
| |
| |
| break; |
| |
| }else if(node instanceof ArtifactNode){ |
| ArtifactNode an=(ArtifactNode) node; |
| List<NBVersionInfo> infos = an.getVersionInfos(); |
| nbvi = infos.isEmpty() ? null : infos.get(0); |
| } |
| } |
| if(nbvi!=null){ |
| lblSelected.setText(nbvi.getGroupId()+" : "+nbvi.getArtifactId() |
| +" : "+nbvi.getVersion()+ " [ " + nbvi.getType() |
| + (nbvi.getClassifier() != null ? ("," + nbvi.getClassifier()) : "")+" ]"); |
| }else{ |
| lblSelected.setText(null); |
| } |
| addButton.setEnabled(nbvi!=null); |
| |
| } |
| } |
| }); |
| resultsRootNode = new ResultsRootNode(); |
| explorerManager.setRootContext(resultsRootNode); |
| createSearchTask(); |
| load(); |
| txtClassName.getDocument().addDocumentListener(new DocumentListener() { |
| @Override |
| public void insertUpdate(DocumentEvent e) { |
| load(); |
| } |
| |
| @Override |
| public void removeUpdate(DocumentEvent e) { |
| load(); |
| } |
| |
| @Override |
| public void changedUpdate(DocumentEvent e) { |
| load(); |
| } |
| |
| }); |
| } |
| |
| public NBVersionInfo getSelectedVersion() { |
| |
| return nbvi; |
| } |
| |
| public JButton getAddButton() { |
| return addButton; |
| } |
| |
| private class Tsk implements Runnable, Cancellable { |
| private volatile boolean cancelled = false; |
| |
| @Override |
| public void run() { |
| if (cancelled) return; |
| |
| final String[] search = new String[1]; |
| try { |
| SwingUtilities.invokeAndWait(new Runnable() { |
| @Override |
| public void run() { |
| lblSelected.setText(null); |
| search[0] = getClassSearchName(); |
| //for debugging purposes only lblMatchingArtifacts.setText(search[0]); |
| } |
| }); |
| } catch (Exception ex) { |
| Exceptions.printStackTrace(ex); |
| } |
| |
| SwingUtilities.invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| resultsRootNode.setOneChild(getSearchingNode()); |
| } |
| }); |
| |
| if (search[0].length() > 0) { |
| |
| try { |
| Result<NBVersionInfo> result = RepositoryQueries.findVersionsByClassResult(search[0], RepositoryPreferences.getInstance().getRepositoryInfos()); |
| if (cancelled) return; |
| updateResult(result.getResults(), result.isPartial()); |
| if (result.isPartial()) { |
| result.waitForSkipped(); |
| if (cancelled) return; |
| updateResult(result.getResults(), false); |
| } |
| } catch (BooleanQuery.TooManyClauses exc) { |
| if (cancelled) return; |
| SwingUtilities.invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| resultsRootNode.setOneChild(getTooGeneralNode()); |
| } |
| }); |
| } catch (OutOfMemoryError oome) { |
| if (cancelled) return; |
| // running into OOME may still happen in Lucene despite the fact that |
| // we are trying hard to prevent it in NexusRepositoryIndexerImpl |
| // (see #190265) |
| // in the bad circumstances theoretically any thread may encounter OOME |
| // but most probably this thread will be it |
| // trying to indicate the condition to the user here |
| SwingUtilities.invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| resultsRootNode.setOneChild(getTooGeneralNode()); |
| } |
| }); |
| } |
| } else { |
| SwingUtilities.invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| resultsRootNode.setOneChild(getNoResultsNode()); |
| } |
| }); |
| } |
| } |
| |
| @Override |
| public synchronized boolean cancel() { |
| cancelled = true; |
| return true; |
| } |
| |
| } |
| |
| private Task createSearchTask() { |
| if (task != null) { |
| task.cancel(); |
| } |
| task = new Tsk(); |
| return RP.create(task, true); |
| } |
| |
| public final synchronized void load() { |
| createSearchTask().schedule(500); |
| } |
| |
| public String getClassSearchName() { |
| return txtClassName.getText().trim(); |
| } |
| |
| /** This method is called from within the constructor to |
| * initialize the form. |
| * WARNING: Do NOT modify this code. The content of this method is |
| * always regenerated by the Form Editor. |
| */ |
| @SuppressWarnings("unchecked") |
| // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents |
| private void initComponents() { |
| |
| lblClassName = new javax.swing.JLabel(); |
| txtClassName = new javax.swing.JTextField(); |
| treeView = new BeanTreeView(); |
| lblMatchingArtifacts = new javax.swing.JLabel(); |
| lblSelected = new javax.swing.JLabel(); |
| |
| lblClassName.setText(org.openide.util.NbBundle.getMessage(SearchDependencyUI.class, "LBL_Class_Name")); // NOI18N |
| |
| treeView.setBorder(javax.swing.BorderFactory.createEtchedBorder(null, javax.swing.UIManager.getDefaults().getColor("ComboBox.selectionBackground"))); |
| |
| lblMatchingArtifacts.setText(org.openide.util.NbBundle.getMessage(SearchDependencyUI.class, "LBL_Matching_artifacts")); // NOI18N |
| |
| javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); |
| this.setLayout(layout); |
| layout.setHorizontalGroup( |
| layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) |
| .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() |
| .addContainerGap() |
| .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) |
| .addComponent(lblSelected, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 430, javax.swing.GroupLayout.PREFERRED_SIZE) |
| .addComponent(treeView, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 430, Short.MAX_VALUE) |
| .addComponent(txtClassName, javax.swing.GroupLayout.DEFAULT_SIZE, 430, Short.MAX_VALUE) |
| .addComponent(lblMatchingArtifacts, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 430, Short.MAX_VALUE) |
| .addComponent(lblClassName, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 430, Short.MAX_VALUE)) |
| .addContainerGap()) |
| ); |
| layout.setVerticalGroup( |
| layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) |
| .addGroup(layout.createSequentialGroup() |
| .addContainerGap() |
| .addComponent(lblClassName) |
| .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) |
| .addComponent(txtClassName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) |
| .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) |
| .addComponent(lblMatchingArtifacts) |
| .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) |
| .addComponent(treeView, javax.swing.GroupLayout.DEFAULT_SIZE, 214, Short.MAX_VALUE) |
| .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) |
| .addComponent(lblSelected, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE)) |
| ); |
| }// </editor-fold>//GEN-END:initComponents |
| |
| // Variables declaration - do not modify//GEN-BEGIN:variables |
| private javax.swing.JLabel lblClassName; |
| private javax.swing.JLabel lblMatchingArtifacts; |
| private javax.swing.JLabel lblSelected; |
| private javax.swing.JScrollPane treeView; |
| private javax.swing.JTextField txtClassName; |
| // End of variables declaration//GEN-END:variables |
| |
| @Override |
| public ExplorerManager getExplorerManager() { |
| return explorerManager; |
| } |
| |
| private class ResultsRootNode extends AbstractNode { |
| |
| private ResultsRootChildren resultsChildren; |
| |
| public ResultsRootNode() { |
| this(new InstanceContent()); |
| } |
| |
| private ResultsRootNode(InstanceContent content) { |
| super (new ResultsRootChildren(), new AbstractLookup(content)); |
| content.add(this); |
| this.resultsChildren = (ResultsRootChildren) getChildren(); |
| } |
| |
| public void setOneChild(Node n) { |
| List<Node> ch = new ArrayList<Node>(1); |
| ch.add(n); |
| setNewChildren(ch); |
| } |
| |
| public void setNewChildren(List<Node> ch) { |
| resultsChildren.setNewChildren (ch); |
| } |
| } |
| |
| private class ResultsRootChildren extends Children.Keys<Node> { |
| |
| List<Node> myNodes; |
| |
| public ResultsRootChildren() { |
| myNodes = Collections.EMPTY_LIST; |
| } |
| |
| private void setNewChildren(List<Node> ch) { |
| myNodes = ch; |
| refreshList(); |
| } |
| |
| @Override |
| protected void addNotify() { |
| refreshList(); |
| } |
| |
| private void refreshList() { |
| List<Node> keys = new ArrayList(); |
| for (Node node : myNodes) { |
| keys.add(node); |
| } |
| setKeys(keys); |
| } |
| |
| @Override |
| protected Node[] createNodes(Node key) { |
| return new Node[] { key }; |
| } |
| |
| } |
| |
| private static Node noResultsNode, searchingNode, tooGeneralNode; |
| |
| @Messages("Node_Loading=Searching...") |
| private static Node getSearchingNode() { |
| if (searchingNode == null) { |
| AbstractNode nd = new AbstractNode(Children.LEAF) { |
| |
| @Override |
| public Image getIcon(int arg0) { |
| return ImageUtilities.loadImage(WAIT); //NOI18N |
| } |
| |
| @Override |
| public Image getOpenedIcon(int arg0) { |
| return getIcon(arg0); |
| } |
| }; |
| nd.setName("Searching"); //NOI18N |
| |
| nd.setDisplayName(Node_Loading()); //NOI18N |
| |
| searchingNode = nd; |
| } |
| return new FilterNode (searchingNode, Children.LEAF); |
| } |
| |
| @Messages("Node_Empty=No matching items") |
| public static Node getNoResultsNode() { |
| if (noResultsNode == null) { |
| AbstractNode nd = new AbstractNode(Children.LEAF) { |
| |
| @Override |
| public Image getIcon(int arg0) { |
| return ImageUtilities.loadImage(EMPTY_IMAGE); //NOI18N |
| } |
| |
| @Override |
| public Image getOpenedIcon(int arg0) { |
| return getIcon(arg0); |
| } |
| }; |
| nd.setName("Empty"); //NOI18N |
| |
| nd.setDisplayName(Node_Empty()); |
| |
| noResultsNode = nd; |
| } |
| return new FilterNode (noResultsNode, Children.LEAF); |
| } |
| |
| @Messages("Node_TooGeneral=Too general query") |
| private static Node getTooGeneralNode() { |
| if (tooGeneralNode == null) { |
| AbstractNode nd = new AbstractNode(Children.LEAF) { |
| |
| @Override |
| public Image getIcon(int arg0) { |
| return ImageUtilities.loadImage(EMPTY_IMAGE); //NOI18N |
| } |
| |
| @Override |
| public Image getOpenedIcon(int arg0) { |
| return getIcon(arg0); |
| } |
| }; |
| nd.setName("Too General"); //NOI18N |
| |
| nd.setDisplayName(Node_TooGeneral()); //NOI18N |
| |
| tooGeneralNode = nd; |
| } |
| |
| return new FilterNode (tooGeneralNode, Children.LEAF); |
| } |
| |
| |
| void updateResult(List<NBVersionInfo> infos, final boolean partial) { |
| final Map<String, List<NBVersionInfo>> map = new HashMap<String, List<NBVersionInfo>>(); |
| |
| for (NBVersionInfo ver : infos) { |
| String key = ver.getGroupId() + " : " + ver.getArtifactId(); |
| List<NBVersionInfo> get = map.get(key); |
| if (get == null) { |
| get = new ArrayList<NBVersionInfo>(); |
| map.put(key, get); |
| } |
| get.add(ver); |
| } |
| final List<String> keyList = new ArrayList<String>(map.keySet()); |
| // sort specially using our comparator, see compare method |
| Collections.sort(keyList, new HeuristicsComparator()); |
| |
| SwingUtilities.invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| updateResultNodes(keyList, map, partial); |
| } |
| }); |
| } |
| |
| private void updateResultNodes(List<String> keyList, Map<String, List<NBVersionInfo>> map, boolean partial) { |
| |
| if (keyList.size() > 0) { // some results available |
| |
| Map<String, Node> currentNodes = new HashMap<String, Node>(); |
| for (Node nd : resultsRootNode.getChildren().getNodes()) { |
| currentNodes.put(nd.getName(), nd); |
| } |
| List<Node> newNodes = new ArrayList<Node>(keyList.size()); |
| |
| for (String key : keyList) { |
| Node nd; |
| nd = currentNodes.get(key); |
| if (null != nd) { |
| ((ArtifactNode)nd).setVersionInfos(map.get(key)); |
| } else { |
| nd = new ArtifactNode(key, map.get(key)); |
| } |
| newNodes.add(nd); |
| } |
| |
| // still searching? |
| if (partial) { |
| newNodes.add(getSearchingNode()); |
| } |
| |
| resultsRootNode.setNewChildren(newNodes); |
| } else if (partial) { // still searching, no results yet |
| resultsRootNode.setOneChild(getSearchingNode()); |
| } else { // finished searching with no results |
| resultsRootNode.setOneChild(getNoResultsNode()); |
| } |
| } |
| |
| //TODO |
| // for netbeans projects, org.netbeans.api is the prefered item in the list |
| // for web/ejb/ear projects, javax.* are probably preferred. |
| // possibly items from groupids that are already present in the pom should also be |
| // put up front. |
| private class HeuristicsComparator implements Comparator<String> { |
| private final Set<String> privilegedGroupIds = new HashSet<String>(); |
| |
| private HeuristicsComparator() { |
| String packaging = project.getLookup().lookup(NbMavenProject.class).getPackagingType(); |
| if (NbMavenProject.TYPE_NBM.equalsIgnoreCase(packaging)) { |
| privilegedGroupIds.add("org.netbeans.api"); //NOI18N |
| } |
| if (NbMavenProject.TYPE_WAR.equalsIgnoreCase(packaging) || |
| NbMavenProject.TYPE_EAR.equalsIgnoreCase(packaging) || |
| NbMavenProject.TYPE_EJB.equalsIgnoreCase(packaging)) { |
| privilegedGroupIds.add("javax.activation");//NOI18N |
| privilegedGroupIds.add("javax.ejb");//NOI18N |
| privilegedGroupIds.add("javax.faces");//NOI18N |
| privilegedGroupIds.add("javax.j2ee");//NOI18N |
| privilegedGroupIds.add("javax.jdo");//NOI18N |
| privilegedGroupIds.add("javax.jms");//NOI18N |
| privilegedGroupIds.add("javax.mail");//NOI18N |
| privilegedGroupIds.add("javax.management");//NOI18N |
| privilegedGroupIds.add("javax.naming");//NOI18N |
| privilegedGroupIds.add("javax.persistence");//NOI18N |
| privilegedGroupIds.add("javax.portlet");//NOI18N |
| privilegedGroupIds.add("javax.resource");//NOI18N |
| privilegedGroupIds.add("javax.security");//NOI18N |
| privilegedGroupIds.add("javax.servlet");//NOI18N |
| privilegedGroupIds.add("javax.sql");//NOI18N |
| privilegedGroupIds.add("javax.transaction");//NOI18N |
| privilegedGroupIds.add("javax.xml");//NOI18N |
| } |
| //TODO add some more heuristics |
| NbMavenProject mavenproject = project.getLookup().lookup(NbMavenProject.class); |
| List<Dependency> deps = mavenproject.getMavenProject().getDependencies(); |
| for (Dependency d : deps) { |
| privilegedGroupIds.add(d.getGroupId()); |
| } |
| } |
| |
| @Override |
| public int compare(String s1, String s2) { |
| String[] split1 = s1.split(":"); |
| String[] split2 = s2.split(":"); |
| boolean b1 = privilegedGroupIds.contains(split1[0].trim()); |
| boolean b2 = privilegedGroupIds.contains(split2[0].trim()); |
| if (b1 && !b2) { |
| return -1; |
| } |
| if (!b1 && b2) { |
| return 1; |
| } |
| return s1.compareTo(s2); |
| } |
| |
| } |
| |
| } |