FilterTopComponent.java revision 1472:c18cbe5936b8
1/* 2 * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24package com.sun.hotspot.igv.filterwindow; 25 26import com.sun.hotspot.igv.filterwindow.actions.MoveFilterDownAction; 27import com.sun.hotspot.igv.filterwindow.actions.MoveFilterUpAction; 28import com.sun.hotspot.igv.filterwindow.actions.NewFilterAction; 29import com.sun.hotspot.igv.filterwindow.actions.RemoveFilterAction; 30import com.sun.hotspot.igv.filterwindow.actions.RemoveFilterSettingsAction; 31import com.sun.hotspot.igv.filterwindow.actions.SaveFilterSettingsAction; 32import com.sun.hotspot.igv.filter.CustomFilter; 33import com.sun.hotspot.igv.filter.Filter; 34import com.sun.hotspot.igv.filter.FilterChain; 35import com.sun.hotspot.igv.filter.FilterSetting; 36import com.sun.hotspot.igv.data.ChangedEvent; 37import com.sun.hotspot.igv.data.ChangedListener; 38import java.awt.BorderLayout; 39import java.awt.event.ActionEvent; 40import java.awt.event.ActionListener; 41import java.io.BufferedReader; 42import java.io.FileNotFoundException; 43import java.io.IOException; 44import java.io.InputStream; 45import java.io.InputStreamReader; 46import java.io.ObjectInput; 47import java.io.ObjectOutput; 48import java.io.OutputStream; 49import java.io.OutputStreamWriter; 50import java.io.Serializable; 51import java.io.Writer; 52import java.util.ArrayList; 53import java.util.Collections; 54import java.util.Comparator; 55import java.util.HashMap; 56import java.util.HashSet; 57import java.util.List; 58import java.util.Set; 59import javax.swing.JComboBox; 60import javax.swing.UIManager; 61import javax.swing.border.Border; 62import org.openide.DialogDisplayer; 63import org.openide.ErrorManager; 64import org.openide.NotifyDescriptor; 65import org.openide.awt.ToolbarPool; 66import org.openide.explorer.ExplorerManager; 67import org.openide.explorer.ExplorerUtils; 68import org.openide.nodes.AbstractNode; 69import org.openide.nodes.Children; 70import org.openide.nodes.Node; 71import org.openide.util.Exceptions; 72import org.openide.util.Lookup; 73import org.openide.util.LookupEvent; 74import org.openide.util.LookupListener; 75import org.openide.util.NbBundle; 76import org.openide.util.Utilities; 77import org.openide.awt.Toolbar; 78import org.openide.filesystems.FileLock; 79import org.openide.util.actions.SystemAction; 80import org.openide.windows.TopComponent; 81import org.openide.windows.WindowManager; 82import org.openide.filesystems.Repository; 83import org.openide.filesystems.FileSystem; 84import org.openide.filesystems.FileObject; 85 86/** 87 * 88 * @author Thomas Wuerthinger 89 */ 90public final class FilterTopComponent extends TopComponent implements LookupListener, ExplorerManager.Provider { 91 92 private static FilterTopComponent instance; 93 public static final String FOLDER_ID = "Filters"; 94 public static final String AFTER_ID = "after"; 95 public static final String ENABLED_ID = "enabled"; 96 public static final String PREFERRED_ID = "FilterTopComponent"; 97 private CheckListView view; 98 private ExplorerManager manager; 99 private FilterChain filterChain; 100 private FilterChain sequence; 101 private Lookup.Result result; 102 private JComboBox comboBox; 103 private List<FilterSetting> filterSettings; 104 private FilterSetting customFilterSetting = new FilterSetting("-- Custom --"); 105 private ChangedEvent<FilterTopComponent> filterSettingsChangedEvent; 106 private ActionListener comboBoxActionListener = new ActionListener() { 107 108 public void actionPerformed(ActionEvent e) { 109 comboBoxSelectionChanged(); 110 } 111 }; 112 113 public ChangedEvent<FilterTopComponent> getFilterSettingsChangedEvent() { 114 return filterSettingsChangedEvent; 115 } 116 117 public FilterChain getSequence() { 118 return sequence; 119 } 120 121 public void updateSelection() { 122 Node[] nodes = this.getExplorerManager().getSelectedNodes(); 123 int[] arr = new int[nodes.length]; 124 for (int i = 0; i < nodes.length; i++) { 125 int index = sequence.getFilters().indexOf(((FilterNode) nodes[i]).getFilter()); 126 arr[i] = index; 127 } 128 view.showSelection(arr); 129 } 130 131 private void comboBoxSelectionChanged() { 132 133 Object o = comboBox.getSelectedItem(); 134 if (o == null) { 135 return; 136 } 137 assert o instanceof FilterSetting; 138 FilterSetting s = (FilterSetting) o; 139 140 if (s != customFilterSetting) { 141 FilterChain chain = getFilterChain(); 142 chain.beginAtomic(); 143 List<Filter> toRemove = new ArrayList<Filter>(); 144 for (Filter f : chain.getFilters()) { 145 if (!s.containsFilter(f)) { 146 toRemove.add(f); 147 } 148 } 149 for (Filter f : toRemove) { 150 chain.removeFilter(f); 151 } 152 153 for (Filter f : s.getFilters()) { 154 if (!chain.containsFilter(f)) { 155 chain.addFilter(f); 156 } 157 } 158 159 chain.endAtomic(); 160 filterSettingsChangedEvent.fire(); 161 } else { 162 this.updateComboBoxSelection(); 163 } 164 165 SystemAction.get(RemoveFilterSettingsAction.class).setEnabled(comboBox.getSelectedItem() != this.customFilterSetting); 166 SystemAction.get(SaveFilterSettingsAction.class).setEnabled(comboBox.getSelectedItem() == this.customFilterSetting); 167 } 168 169 private void updateComboBox() { 170 comboBox.removeAllItems(); 171 comboBox.addItem(customFilterSetting); 172 for (FilterSetting s : filterSettings) { 173 comboBox.addItem(s); 174 } 175 176 this.updateComboBoxSelection(); 177 } 178 179 public void addFilterSetting() { 180 NotifyDescriptor.InputLine l = new NotifyDescriptor.InputLine("Enter a name:", "Filter"); 181 if (DialogDisplayer.getDefault().notify(l) == NotifyDescriptor.OK_OPTION) { 182 String name = l.getInputText(); 183 184 FilterSetting toRemove = null; 185 for (FilterSetting s : filterSettings) { 186 if (s.getName().equals(name)) { 187 NotifyDescriptor.Confirmation conf = new NotifyDescriptor.Confirmation("Filter \"" + name + "\" already exists, to you want to overwrite?", "Filter"); 188 if (DialogDisplayer.getDefault().notify(conf) == NotifyDescriptor.YES_OPTION) { 189 toRemove = s; 190 break; 191 } else { 192 return; 193 } 194 } 195 } 196 197 if (toRemove != null) { 198 filterSettings.remove(toRemove); 199 } 200 FilterSetting setting = createFilterSetting(name); 201 filterSettings.add(setting); 202 203 // Sort alphabetically 204 Collections.sort(filterSettings, new Comparator<FilterSetting>() { 205 206 public int compare(FilterSetting o1, FilterSetting o2) { 207 return o1.getName().compareTo(o2.getName()); 208 } 209 }); 210 211 updateComboBox(); 212 } 213 } 214 215 public boolean canRemoveFilterSetting() { 216 return comboBox.getSelectedItem() != customFilterSetting; 217 } 218 219 public void removeFilterSetting() { 220 if (canRemoveFilterSetting()) { 221 Object o = comboBox.getSelectedItem(); 222 assert o instanceof FilterSetting; 223 FilterSetting f = (FilterSetting) o; 224 assert f != customFilterSetting; 225 assert filterSettings.contains(f); 226 NotifyDescriptor.Confirmation l = new NotifyDescriptor.Confirmation("Do you really want to remove filter \"" + f + "\"?", "Filter"); 227 if (DialogDisplayer.getDefault().notify(l) == NotifyDescriptor.YES_OPTION) { 228 filterSettings.remove(f); 229 updateComboBox(); 230 } 231 } 232 } 233 234 private FilterSetting createFilterSetting(String name) { 235 FilterSetting s = new FilterSetting(name); 236 FilterChain chain = this.getFilterChain(); 237 for (Filter f : chain.getFilters()) { 238 s.addFilter(f); 239 } 240 return s; 241 } 242 243 private void updateComboBoxSelection() { 244 List<Filter> filters = this.getFilterChain().getFilters(); 245 boolean found = false; 246 for (FilterSetting s : filterSettings) { 247 if (s.getFilterCount() == filters.size()) { 248 boolean ok = true; 249 for (Filter f : filters) { 250 if (!s.containsFilter(f)) { 251 ok = false; 252 } 253 } 254 255 if (ok) { 256 if (comboBox.getSelectedItem() != s) { 257 comboBox.setSelectedItem(s); 258 } 259 found = true; 260 break; 261 } 262 } 263 } 264 265 if (!found && comboBox.getSelectedItem() != customFilterSetting) { 266 comboBox.setSelectedItem(customFilterSetting); 267 } 268 } 269 270 private class FilterChildren extends Children.Keys implements ChangedListener<CheckNode> { 271 272 //private Node[] oldSelection; 273 //private ArrayList<Node> newSelection; 274 private HashMap<Object, Node> nodeHash = new HashMap<Object, Node>(); 275 276 protected Node[] createNodes(Object object) { 277 if (nodeHash.containsKey(object)) { 278 return new Node[]{nodeHash.get(object)}; 279 } 280 281 assert object instanceof Filter; 282 Filter filter = (Filter) object; 283 com.sun.hotspot.igv.filterwindow.FilterNode node = new com.sun.hotspot.igv.filterwindow.FilterNode(filter); 284 node.getSelectionChangedEvent().addListener(this); 285 nodeHash.put(object, node); 286 return new Node[]{node}; 287 } 288 289 public FilterChildren() { 290 sequence.getChangedEvent().addListener(new ChangedListener<FilterChain>() { 291 292 public void changed(FilterChain source) { 293 addNotify(); 294 } 295 }); 296 297 setBefore(false); 298 } 299 300 protected void addNotify() { 301 setKeys(sequence.getFilters()); 302 updateSelection(); 303 } 304 305 public void changed(CheckNode source) { 306 FilterNode node = (FilterNode) source; 307 Filter f = node.getFilter(); 308 FilterChain chain = getFilterChain(); 309 if (node.isSelected()) { 310 if (!chain.containsFilter(f)) { 311 chain.addFilter(f); 312 } 313 } else { 314 if (chain.containsFilter(f)) { 315 chain.removeFilter(f); 316 } 317 } 318 view.revalidate(); 319 view.repaint(); 320 updateComboBoxSelection(); 321 } 322 } 323 324 public FilterChain getFilterChain() { 325 return filterChain;/* 326 EditorTopComponent tc = EditorTopComponent.getActive(); 327 if (tc == null) { 328 return filterChain; 329 } 330 return tc.getFilterChain();*/ 331 } 332 333 private FilterTopComponent() { 334 filterSettingsChangedEvent = new ChangedEvent<FilterTopComponent>(this); 335 initComponents(); 336 setName(NbBundle.getMessage(FilterTopComponent.class, "CTL_FilterTopComponent")); 337 setToolTipText(NbBundle.getMessage(FilterTopComponent.class, "HINT_FilterTopComponent")); 338 // setIcon(Utilities.loadImage(ICON_PATH, true)); 339 340 sequence = new FilterChain(); 341 filterChain = new FilterChain(); 342 initFilters(); 343 manager = new ExplorerManager(); 344 manager.setRootContext(new AbstractNode(new FilterChildren())); 345 associateLookup(ExplorerUtils.createLookup(manager, getActionMap())); 346 view = new CheckListView(); 347 348 ToolbarPool.getDefault().setPreferredIconSize(16); 349 Toolbar toolBar = new Toolbar(); 350 Border b = (Border) UIManager.get("Nb.Editor.Toolbar.border"); //NOI18N 351 toolBar.setBorder(b); 352 comboBox = new JComboBox(); 353 toolBar.add(comboBox); 354 this.add(toolBar, BorderLayout.NORTH); 355 toolBar.add(SaveFilterSettingsAction.get(SaveFilterSettingsAction.class)); 356 toolBar.add(RemoveFilterSettingsAction.get(RemoveFilterSettingsAction.class)); 357 toolBar.addSeparator(); 358 toolBar.add(MoveFilterUpAction.get(MoveFilterUpAction.class).createContextAwareInstance(this.getLookup())); 359 toolBar.add(MoveFilterDownAction.get(MoveFilterDownAction.class).createContextAwareInstance(this.getLookup())); 360 toolBar.add(RemoveFilterAction.get(RemoveFilterAction.class).createContextAwareInstance(this.getLookup())); 361 toolBar.add(NewFilterAction.get(NewFilterAction.class)); 362 this.add(view, BorderLayout.CENTER); 363 364 filterSettings = new ArrayList<FilterSetting>(); 365 updateComboBox(); 366 367 comboBox.addActionListener(comboBoxActionListener); 368 setChain(filterChain); 369 } 370 371 public void newFilter() { 372 CustomFilter cf = new CustomFilter("My custom filter", ""); 373 if (cf.openInEditor()) { 374 sequence.addFilter(cf); 375 FileObject fo = getFileObject(cf); 376 FilterChangedListener listener = new FilterChangedListener(fo, cf); 377 listener.changed(cf); 378 cf.getChangedEvent().addListener(listener); 379 } 380 } 381 382 public void removeFilter(Filter f) { 383 com.sun.hotspot.igv.filter.CustomFilter cf = (com.sun.hotspot.igv.filter.CustomFilter) f; 384 385 sequence.removeFilter(cf); 386 try { 387 getFileObject(cf).delete(); 388 } catch (IOException ex) { 389 Exceptions.printStackTrace(ex); 390 } 391 392 } 393 394 private static class FilterChangedListener implements ChangedListener<Filter> { 395 396 private FileObject fileObject; 397 private CustomFilter filter; 398 399 public FilterChangedListener(FileObject fo, CustomFilter cf) { 400 fileObject = fo; 401 filter = cf; 402 } 403 404 public void changed(Filter source) { 405 try { 406 if (!fileObject.getName().equals(filter.getName())) { 407 FileLock lock = fileObject.lock(); 408 fileObject.move(lock, fileObject.getParent(), filter.getName(), ""); 409 lock.releaseLock(); 410 FileObject newFileObject = fileObject.getParent().getFileObject(filter.getName()); 411 fileObject = newFileObject; 412 413 } 414 415 FileLock lock = fileObject.lock(); 416 OutputStream os = fileObject.getOutputStream(lock); 417 Writer w = new OutputStreamWriter(os); 418 String s = filter.getCode(); 419 w.write(s); 420 w.close(); 421 lock.releaseLock(); 422 423 } catch (IOException ex) { 424 Exceptions.printStackTrace(ex); 425 } 426 } 427 } 428 429 public void initFilters() { 430 431 FileSystem fs = Repository.getDefault().getDefaultFileSystem(); 432 FileObject folder = fs.getRoot().getFileObject(FOLDER_ID); 433 FileObject[] children = folder.getChildren(); 434 435 List<CustomFilter> customFilters = new ArrayList<CustomFilter>(); 436 HashMap<CustomFilter, String> afterMap = new HashMap<CustomFilter, String>(); 437 Set<CustomFilter> enabledSet = new HashSet<CustomFilter>(); 438 HashMap<String, CustomFilter> map = new HashMap<String, CustomFilter>(); 439 440 for (final FileObject fo : children) { 441 InputStream is = null; 442 443 String code = ""; 444 FileLock lock = null; 445 try { 446 lock = fo.lock(); 447 is = fo.getInputStream(); 448 BufferedReader r = new BufferedReader(new InputStreamReader(is)); 449 String s; 450 StringBuffer sb = new StringBuffer(); 451 while ((s = r.readLine()) != null) { 452 sb.append(s); 453 sb.append("\n"); 454 } 455 code = sb.toString(); 456 457 } catch (FileNotFoundException ex) { 458 Exceptions.printStackTrace(ex); 459 } catch (IOException ex) { 460 Exceptions.printStackTrace(ex); 461 } finally { 462 try { 463 is.close(); 464 } catch (IOException ex) { 465 Exceptions.printStackTrace(ex); 466 } 467 lock.releaseLock(); 468 } 469 470 String displayName = fo.getName(); 471 472 473 final CustomFilter cf = new CustomFilter(displayName, code); 474 map.put(displayName, cf); 475 476 String after = (String) fo.getAttribute(AFTER_ID); 477 afterMap.put(cf, after); 478 479 Boolean enabled = (Boolean) fo.getAttribute(ENABLED_ID); 480 if (enabled != null && (boolean) enabled) { 481 enabledSet.add(cf); 482 } 483 484 cf.getChangedEvent().addListener(new FilterChangedListener(fo, cf)); 485 486 customFilters.add(cf); 487 } 488 489 for (int j = 0; j < customFilters.size(); j++) { 490 for (int i = 0; i < customFilters.size(); i++) { 491 List<CustomFilter> copiedList = new ArrayList<CustomFilter>(customFilters); 492 for (CustomFilter cf : copiedList) { 493 494 String after = afterMap.get(cf); 495 496 if (map.containsKey(after)) { 497 CustomFilter afterCf = map.get(after); 498 int index = customFilters.indexOf(afterCf); 499 int currentIndex = customFilters.indexOf(cf); 500 501 if (currentIndex < index) { 502 customFilters.remove(currentIndex); 503 customFilters.add(index, cf); 504 } 505 } 506 } 507 } 508 } 509 510 for (CustomFilter cf : customFilters) { 511 sequence.addFilter(cf); 512 if (enabledSet.contains(cf)) { 513 filterChain.addFilter(cf); 514 } 515 } 516 } 517 518 /** This method is called from within the constructor to 519 * initialize the form. 520 * WARNING: Do NOT modify this code. The content of this method is 521 * always regenerated by the Form Editor. 522 */ 523 // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents 524 private void initComponents() { 525 526 setLayout(new java.awt.BorderLayout()); 527 528 }// </editor-fold>//GEN-END:initComponents 529 // Variables declaration - do not modify//GEN-BEGIN:variables 530 // End of variables declaration//GEN-END:variables 531 /** 532 * Gets default instance. Do not use directly: reserved for *.settings files only, 533 * i.e. deserialization routines; otherwise you could get a non-deserialized instance. 534 * To obtain the singleton instance, use {@link findInstance}. 535 */ 536 public static synchronized FilterTopComponent getDefault() { 537 if (instance == null) { 538 instance = new FilterTopComponent(); 539 } 540 return instance; 541 } 542 543 /** 544 * Obtain the FilterTopComponent instance. Never call {@link #getDefault} directly! 545 */ 546 public static synchronized FilterTopComponent findInstance() { 547 TopComponent win = WindowManager.getDefault().findTopComponent(PREFERRED_ID); 548 if (win == null) { 549 ErrorManager.getDefault().log(ErrorManager.WARNING, "Cannot find Filter component. It will not be located properly in the window system."); 550 return getDefault(); 551 } 552 if (win instanceof FilterTopComponent) { 553 return (FilterTopComponent) win; 554 } 555 ErrorManager.getDefault().log(ErrorManager.WARNING, "There seem to be multiple components with the '" + PREFERRED_ID + "' ID. That is a potential source of errors and unexpected behavior."); 556 return getDefault(); 557 } 558 559 @Override 560 public int getPersistenceType() { 561 return TopComponent.PERSISTENCE_ALWAYS; 562 } 563 564 @Override 565 protected String preferredID() { 566 return PREFERRED_ID; 567 } 568 569 @Override 570 public ExplorerManager getExplorerManager() { 571 return manager; 572 } 573 574 @Override 575 public void componentOpened() { 576 Lookup.Template<FilterChain> tpl = new Lookup.Template<FilterChain>(FilterChain.class); 577 result = Utilities.actionsGlobalContext().lookup(tpl); 578 result.addLookupListener(this); 579 } 580 581 @Override 582 public void componentClosed() { 583 result.removeLookupListener(this); 584 result = null; 585 } 586 587 public void resultChanged(LookupEvent lookupEvent) { 588 setChain(Utilities.actionsGlobalContext().lookup(FilterChain.class)); 589 /* 590 EditorTopComponent tc = EditorTopComponent.getActive(); 591 if (tc != null) { 592 setChain(tc.getFilterChain()); 593 }*/ 594 } 595 596 public void setChain(FilterChain chain) { 597 updateComboBoxSelection(); 598 } 599 600 private FileObject getFileObject(CustomFilter cf) { 601 FileObject fo = Repository.getDefault().getDefaultFileSystem().getRoot().getFileObject(FOLDER_ID + "/" + cf.getName()); 602 if (fo == null) { 603 try { 604 fo = org.openide.filesystems.Repository.getDefault().getDefaultFileSystem().getRoot().getFileObject(FOLDER_ID).createData(cf.getName()); 605 } catch (IOException ex) { 606 Exceptions.printStackTrace(ex); 607 } 608 } 609 return fo; 610 } 611 612 @Override 613 public void writeExternal(ObjectOutput out) throws IOException { 614 super.writeExternal(out); 615 616 out.writeInt(filterSettings.size()); 617 for (FilterSetting f : filterSettings) { 618 out.writeUTF(f.getName()); 619 620 out.writeInt(f.getFilterCount()); 621 for (Filter filter : f.getFilters()) { 622 CustomFilter cf = (CustomFilter) filter; 623 out.writeUTF(cf.getName()); 624 } 625 } 626 627 CustomFilter prev = null; 628 for (Filter f : this.sequence.getFilters()) { 629 CustomFilter cf = (CustomFilter) f; 630 FileObject fo = getFileObject(cf); 631 if (getFilterChain().containsFilter(cf)) { 632 fo.setAttribute(ENABLED_ID, true); 633 } else { 634 fo.setAttribute(ENABLED_ID, false); 635 } 636 637 if (prev == null) { 638 fo.setAttribute(AFTER_ID, null); 639 } else { 640 fo.setAttribute(AFTER_ID, prev.getName()); 641 } 642 643 prev = cf; 644 } 645 } 646 647 public CustomFilter findFilter(String name) { 648 for (Filter f : sequence.getFilters()) { 649 650 CustomFilter cf = (CustomFilter) f; 651 if (cf.getName().equals(name)) { 652 return cf; 653 } 654 } 655 656 return null; 657 } 658 659 @Override 660 public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { 661 super.readExternal(in); 662 663 int filterSettingsCount = in.readInt(); 664 for (int i = 0; i < filterSettingsCount; i++) { 665 String name = in.readUTF(); 666 FilterSetting s = new FilterSetting(name); 667 int filterCount = in.readInt(); 668 for (int j = 0; j < filterCount; j++) { 669 String filterName = in.readUTF(); 670 CustomFilter filter = findFilter(filterName); 671 if (filter != null) { 672 s.addFilter(filter); 673 } 674 } 675 676 filterSettings.add(s); 677 } 678 updateComboBox(); 679 } 680 681 final static class ResolvableHelper implements Serializable { 682 683 private static final long serialVersionUID = 1L; 684 685 public Object readResolve() { 686 return FilterTopComponent.getDefault(); 687 } 688 } 689} 690