/* * Copyright 2016 Igor Maznitsa. * * Licensed 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 com.igormaznitsa.sciareto.ui; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.InputStreamReader; import java.io.Reader; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.concurrent.atomic.AtomicReference; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.swing.ListModel; import javax.swing.SwingUtilities; import javax.swing.event.ListDataEvent; import javax.swing.event.ListDataListener; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.io.LineIterator; import com.igormaznitsa.meta.annotation.MustNotContainNull; import com.igormaznitsa.mindmap.model.MindMap; import com.igormaznitsa.mindmap.model.logger.Logger; import com.igormaznitsa.mindmap.model.logger.LoggerFactory; import com.igormaznitsa.sciareto.Context; import com.igormaznitsa.sciareto.ui.misc.NodeListRenderer; import com.igormaznitsa.sciareto.ui.tree.NodeFileOrFolder; import com.igormaznitsa.sciareto.ui.tree.NodeProject; public class FindUsagesPanel extends javax.swing.JPanel { private static final long serialVersionUID = -2670972411220199031L; private final AtomicReference<Thread> searchingThread = new AtomicReference<>(); private static final Logger LOGGER = LoggerFactory.getLogger(FindUsagesPanel.class); private final transient List<NodeFileOrFolder> foundFiles = new ArrayList<>(); private final transient List<ListDataListener> listListeners = new ArrayList<>(); private final String fullNormalizedPath; private final boolean findEverywhere; public FindUsagesPanel(@Nonnull final Context context, @Nonnull final NodeFileOrFolder itemToFind, final boolean findEverywhere) { initComponents(); this.findEverywhere = findEverywhere; final File asfile = itemToFind.makeFileForNode(); if (asfile == null) { LOGGER.error("Can't get file for node " + itemToFind); //NOI18N throw new IllegalArgumentException("Can't get file for node"); //NOI18N } this.fullNormalizedPath = FilenameUtils.normalize(asfile.getAbsolutePath()); this.textFieldSearchPath.setText(this.fullNormalizedPath); this.textFieldSearchPath.setEnabled(false); this.listOfFoundElements.setCellRenderer(new NodeListRenderer()); this.listOfFoundElements.setModel(new ListModel<NodeFileOrFolder>() { @Override public int getSize() { return foundFiles.size(); } @Override @Nonnull public NodeFileOrFolder getElementAt(final int index) { return foundFiles.get(index); } @Override public void addListDataListener(@Nonnull final ListDataListener l) { listListeners.add(l); } @Override public void removeListDataListener(@Nonnull final ListDataListener l) { listListeners.remove(l); } }); final List<NodeProject> scope = new ArrayList<>(); final NodeProject project = itemToFind.findProject(); for (final NodeFileOrFolder p : context.getCurrentGroup()) { scope.add((NodeProject) p); } startSearchThread(scope, itemToFind); } @Nullable public NodeFileOrFolder getSelected() { return this.listOfFoundElements.getSelectedValue(); } private void addFileIntoList(@Nonnull final NodeFileOrFolder file) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { final boolean first = foundFiles.isEmpty(); foundFiles.add(file); for (final ListDataListener l : listListeners) { l.intervalAdded(new ListDataEvent(listOfFoundElements, ListDataEvent.INTERVAL_ADDED, foundFiles.size() - 1, foundFiles.size() - 1)); } if (first){ listOfFoundElements.setSelectedIndex(0); } } }); } private void startSearchThread(@Nonnull @MustNotContainNull final List<NodeProject> scope, @Nonnull final NodeFileOrFolder itemToFind) { int size = 0; for (final NodeProject p : scope) { size += p.size(); } final File nodeFileToSearch = itemToFind.makeFileForNode(); if (nodeFileToSearch == null) { safeSetProgressValue(Integer.MAX_VALUE); } else { final Runnable runnable = new Runnable() { int value = 0; private void processFile(final NodeFileOrFolder file) { value++; final File f = file.makeFileForNode(); final NodeProject project = file.findProject(); if (project != null) { final String extension = FilenameUtils.getExtension(f.getName()).toLowerCase(Locale.ENGLISH); if ("mmd".equals(extension)) { //NOI18N Reader reader = null; try { reader = new BufferedReader(new InputStreamReader(new FileInputStream(f), "UTF-8")); //NOI18N final MindMap map = new MindMap(null, reader); if (!MapUtils.findTopicsRelatedToFile(project.getFolder(), nodeFileToSearch, map).isEmpty()) { addFileIntoList(file); } } catch (Exception ex) { LOGGER.error("Can't parse map", ex); //NOI18N } finally { IOUtils.closeQuietly(reader); } } else if (findEverywhere){ try { final LineIterator lineIterator = org.apache.commons.io.FileUtils.lineIterator(f, "UTF-8"); //NOI18N try { while (lineIterator.hasNext()) { if (Thread.currentThread().isInterrupted()) { return; } final String lineFromFile = lineIterator.nextLine(); if (lineFromFile.contains(fullNormalizedPath)) { addFileIntoList(file); break; } } } finally { LineIterator.closeQuietly(lineIterator); } } catch (Exception ex) { LOGGER.error("Error during text search in file : " + f); //NOI18N } } } if (!Thread.currentThread().isInterrupted()) { safeSetProgressValue(value); } } private void processFolder(final NodeFileOrFolder folder) { value++; for (final NodeFileOrFolder f : folder) { if (f.isLeaf()) { processFile(f); } else { processFolder(f); } } if (!Thread.currentThread().isInterrupted()) { safeSetProgressValue(value); } } @Override public void run() { for (final NodeProject p : scope) { for (final NodeFileOrFolder f : p) { if (Thread.currentThread().isInterrupted()) { return; } if (f.isLeaf()) { processFile(f); } else { processFolder(f); } } } safeSetProgressValue(Integer.MAX_VALUE); } }; final Thread thread = new Thread(runnable, "SciaRetoSearchUsage"); //NOI18N thread.setDaemon(true); final Thread oldThread = this.searchingThread.getAndSet(thread); if (oldThread != null) { oldThread.interrupt(); try { oldThread.join(1000L); } catch (InterruptedException ex) { LOGGER.error("Exception during waiting of search thread interruption", ex); //NOI18N } } this.progressBarSearch.setMinimum(0); this.progressBarSearch.setMaximum(size); this.progressBarSearch.setValue(0); thread.start(); } } private void safeSetProgressValue(final int value) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (value == Integer.MAX_VALUE) { progressBarSearch.setEnabled(false); progressBarSearch.setIndeterminate(false); progressBarSearch.setValue(progressBarSearch.getMaximum()); } else if (value < 0) { progressBarSearch.setEnabled(true); progressBarSearch.setIndeterminate(true); } else { progressBarSearch.setEnabled(true); progressBarSearch.setIndeterminate(false); progressBarSearch.setValue(value); } } }); } public void dispose() { final Thread thread = this.searchingThread.getAndSet(null); if (thread != null) { thread.interrupt(); } } /** * 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() { java.awt.GridBagConstraints gridBagConstraints; jLabel1 = new javax.swing.JLabel(); textFieldSearchPath = new javax.swing.JTextField(); jScrollPane1 = new javax.swing.JScrollPane(); listOfFoundElements = new javax.swing.JList<>(); jPanel1 = new javax.swing.JPanel(); progressBarSearch = new javax.swing.JProgressBar(); setLayout(new java.awt.GridBagLayout()); jLabel1.setText("Search usage of :"); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 0; gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; add(jLabel1, gridBagConstraints); textFieldSearchPath.setEditable(false); textFieldSearchPath.setText("jTextField1"); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 0; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.ipadx = 236; gridBagConstraints.weightx = 1000.0; add(textFieldSearchPath, gridBagConstraints); listOfFoundElements.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); jScrollPane1.setViewportView(listOfFoundElements); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 2; gridBagConstraints.gridwidth = 2; gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints.ipadx = 354; gridBagConstraints.ipady = 229; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.weightx = 1.0; gridBagConstraints.weighty = 1000.0; add(jScrollPane1, gridBagConstraints); jPanel1.setLayout(new java.awt.BorderLayout()); jPanel1.add(progressBarSearch, java.awt.BorderLayout.CENTER); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 1; gridBagConstraints.gridwidth = 2; gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; add(jPanel1, gridBagConstraints); }// </editor-fold>//GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JLabel jLabel1; private javax.swing.JPanel jPanel1; private javax.swing.JScrollPane jScrollPane1; private javax.swing.JList<NodeFileOrFolder> listOfFoundElements; private javax.swing.JProgressBar progressBarSearch; private javax.swing.JTextField textFieldSearchPath; // End of variables declaration//GEN-END:variables }