Java tutorial
/* * Copyright (c) 2012 Alvin R. de Leon. All Rights Reserved. * * 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 org.piraso.ui.base; import org.apache.commons.collections.CollectionUtils; import org.netbeans.api.settings.ConvertAsProperties; import org.openide.awt.ActionID; import org.openide.awt.ActionReference; import org.openide.util.*; import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.AbstractLookup; import org.openide.util.lookup.InstanceContent; import org.openide.windows.TopComponent; import org.piraso.api.entry.Entry; import org.piraso.api.entry.RequestEntry; import org.piraso.api.entry.ResponseEntry; import org.piraso.io.IOEntryEvent; import org.piraso.io.IOEntryListener; import org.piraso.ui.api.manager.FontProviderManager; import org.piraso.ui.api.manager.ModelEvent; import org.piraso.ui.api.manager.ModelOnChangeListener; import org.piraso.ui.api.manager.SingleModelManagers; import org.piraso.ui.api.util.SingleClassInstanceContent; import org.piraso.ui.api.util.WindowUtils; import org.piraso.ui.base.manager.EntryViewProviderManager; import javax.swing.*; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.*; import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.*; /** * Top component which displays something. */ @ConvertAsProperties(dtd = "-//org.piraso.ui.base//RequestTree//EN", autostore = false) @TopComponent.Description(preferredID = "RequestTreeTopComponent", iconBase = "org/piraso/ui/base/icons/folders-explorer-icon.png", persistenceType = TopComponent.PERSISTENCE_ALWAYS) @TopComponent.Registration(mode = "explorer", openAtStartup = true) @ActionID(category = "Window", id = "org.piraso.ui.base.RequestTreeTopComponent") @ActionReference(path = "Menu/Window", position = 338) @TopComponent.OpenActionRegistration(displayName = "#CTL_RequestTreeAction", preferredID = "RequestTreeTopComponent") @Messages({ "CTL_RequestTreeAction=Request Explorer", "CTL_RequestTreeTopComponent=Request Explorer", "HINT_RequestTreeTopComponent=This is a Request Explorer window" }) public final class RequestTreeTopComponent extends TopComponent implements LookupListener { public static final long LATEST_TIME_THRESHOLD = 10000; public static final long SOME_HOW_LATEST_TIME_THRESHOLD = 30000; public static RequestTreeTopComponent get() { Set<TopComponent> opened = TopComponent.getRegistry().getOpened(); for (TopComponent component : opened) { if (RequestTreeTopComponent.class.isInstance(component)) { return (RequestTreeTopComponent) component; } } return null; } private final ModelOnChangeListener GENERAL_SETTINGS_LISTENER = new ModelOnChangeListener() { @Override public void onChange(ModelEvent evt) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { repaint(); invalidate(); } }); } }; private DefaultMutableTreeNode root = new DefaultMutableTreeNode(); private DefaultTreeModel model = new DefaultTreeModel(root); private SingleClassInstanceContent<Entry> entryContent; protected Lookup.Result result = null; protected ContextMonitorDelegate lastActiveMonitor; private final Set<Child> latestChildren = new HashSet<Child>(); private final Map<ContextMonitorDelegate, ContextMonitorHandler> delegateHandlers = new HashMap<ContextMonitorDelegate, ContextMonitorHandler>(); public RequestTreeTopComponent() { setName(Bundle.CTL_RequestTreeTopComponent()); setToolTipText(Bundle.HINT_RequestTreeTopComponent()); initComponents(); InstanceContent content = new InstanceContent(); associateLookup(new AbstractLookup(content)); this.entryContent = new SingleClassInstanceContent<Entry>(content); jTree.setFont(FontProviderManager.INSTANCE.getEditorDefaultFont()); jTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); jTree.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { int selRow = jTree.getRowForLocation(e.getX(), e.getY()); TreePath selPath = jTree.getPathForLocation(e.getX(), e.getY()); if (selRow != -1 && selPath != null) { DefaultMutableTreeNode node = (DefaultMutableTreeNode) selPath.getLastPathComponent(); Child child = null; if (Child.class.isInstance(node.getUserObject())) { child = (Child) node.getUserObject(); } if (child != null && e.getClickCount() == 1) { child.select(); } } } }); jTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() { @Override public void valueChanged(TreeSelectionEvent e) { if (e.getPath() != null && e.getPath().getLastPathComponent() != null) { DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.getPath().getLastPathComponent(); if (node.getUserObject() != null && Child.class.isInstance(node.getUserObject())) { Child child = (Child) node.getUserObject(); child.showRequestView(); } } } }); } /** * 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. */ // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { jScrollPane1 = new javax.swing.JScrollPane(); jTree = new javax.swing.JTree(); toolbar = new javax.swing.JToolBar(); btnCollapse = new javax.swing.JButton(); btnColorLatest = new javax.swing.JToggleButton(); jSeparator1 = new javax.swing.JToolBar.Separator(); btnTarget = new javax.swing.JButton(); setLayout(new java.awt.BorderLayout()); jTree.setModel(model); jTree.setCellRenderer(new TreeCellRenderer()); jTree.setRootVisible(false); jTree.setShowsRootHandles(true); jScrollPane1.setViewportView(jTree); add(jScrollPane1, java.awt.BorderLayout.CENTER); toolbar.setBackground(new java.awt.Color(226, 226, 226)); toolbar.setFloatable(false); toolbar.setRollover(true); toolbar.setOpaque(false); btnCollapse.setIcon(new javax.swing.ImageIcon( getClass().getResource("/org/piraso/ui/base/icons/gh-icon-collapse.png"))); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(btnCollapse, org.openide.util.NbBundle .getMessage(RequestTreeTopComponent.class, "RequestTreeTopComponent.btnCollapse.text")); // NOI18N btnCollapse.setToolTipText(org.openide.util.NbBundle.getMessage(RequestTreeTopComponent.class, "RequestTreeTopComponent.btnCollapse.toolTipText")); // NOI18N btnCollapse.setBorder(javax.swing.BorderFactory.createEmptyBorder(7, 7, 7, 7)); btnCollapse.setFocusable(false); btnCollapse.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); btnCollapse.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); btnCollapse.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { btnCollapseActionPerformed(evt); } }); toolbar.add(btnCollapse); btnColorLatest .setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/piraso/ui/base/icons/time.png"))); // NOI18N btnColorLatest.setSelected(true); org.openide.awt.Mnemonics.setLocalizedText(btnColorLatest, org.openide.util.NbBundle .getMessage(RequestTreeTopComponent.class, "RequestTreeTopComponent.btnColorLatest.text")); // NOI18N btnColorLatest.setToolTipText(org.openide.util.NbBundle.getMessage(RequestTreeTopComponent.class, "RequestTreeTopComponent.btnColorLatest.toolTipText")); // NOI18N btnColorLatest.setFocusable(false); btnColorLatest.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); btnColorLatest.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); btnColorLatest.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { btnColorLatestActionPerformed(evt); } }); toolbar.add(btnColorLatest); toolbar.add(jSeparator1); btnTarget.setIcon( new javax.swing.ImageIcon(getClass().getResource("/org/piraso/ui/base/icons/target_arrow.png"))); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(btnTarget, org.openide.util.NbBundle .getMessage(RequestTreeTopComponent.class, "RequestTreeTopComponent.btnTarget.text")); // NOI18N btnTarget.setToolTipText(org.openide.util.NbBundle.getMessage(RequestTreeTopComponent.class, "RequestTreeTopComponent.btnTarget.toolTipText")); // NOI18N btnTarget.setBorder(javax.swing.BorderFactory.createEmptyBorder(7, 7, 7, 7)); btnTarget.setFocusable(false); btnTarget.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); btnTarget.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); btnTarget.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { btnTargetActionPerformed(evt); } }); toolbar.add(Box.createHorizontalGlue()); toolbar.add(btnTarget); add(toolbar, java.awt.BorderLayout.NORTH); }// </editor-fold>//GEN-END:initComponents private void btnTargetActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnTargetActionPerformed SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (lastActiveMonitor == null || lastActiveMonitor.getSelectedRequest() == null) { return; } if (!((TopComponent) lastActiveMonitor).isOpened()) { lastActiveMonitor = null; return; } RequestEntry entry = lastActiveMonitor.getSelectedRequest(); synchronized (delegateHandlers) { ContextMonitorHandler handler = delegateHandlers.get(lastActiveMonitor); Child child = handler.childMap.get(entry.getBaseRequestId()); if (child != null) { TreePath path = new TreePath(child.node.getPath()); jTree.getSelectionModel().setSelectionPath(path); jTree.expandPath(path); } } } }); }//GEN-LAST:event_btnTargetActionPerformed private void btnColorLatestActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnColorLatestActionPerformed SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (btnColorLatest.isSelected()) { btnColorLatest.setIcon(new javax.swing.ImageIcon( getClass().getResource("/org/piraso/ui/base/icons/time.png"))); btnColorLatest.setToolTipText(NbBundle.getMessage(ContextMonitorTopComponent.class, "RequestTreeTopComponent.btnColorLatest.toolTipText")); } else { btnColorLatest.setIcon(new javax.swing.ImageIcon( getClass().getResource("/org/piraso/ui/base/icons/time-minus.png"))); btnColorLatest.setToolTipText(NbBundle.getMessage(ContextMonitorTopComponent.class, "RequestTreeTopComponent.btnColorLatest.unselected.toolTipText")); } if (!btnColorLatest.isSelected()) { synchronized (latestChildren) { refreshChildrenColor(); } } } }); }//GEN-LAST:event_btnColorLatestActionPerformed private void btnCollapseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCollapseActionPerformed model.nodeStructureChanged(root); }//GEN-LAST:event_btnCollapseActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton btnCollapse; private javax.swing.JToggleButton btnColorLatest; private javax.swing.JButton btnTarget; private javax.swing.JScrollPane jScrollPane1; private javax.swing.JToolBar.Separator jSeparator1; private javax.swing.JTree jTree; private javax.swing.JToolBar toolbar; // End of variables declaration//GEN-END:variables @Override public void componentOpened() { result = Utilities.actionsGlobalContext().lookupResult(ContextMonitorDelegate.class); result.addLookupListener(this); SingleModelManagers.GENERAL_SETTINGS.addModelOnChangeListener(GENERAL_SETTINGS_LISTENER); } @Override public void componentClosed() { result.removeLookupListener(this); result = null; SingleModelManagers.GENERAL_SETTINGS.removeModelOnChangeListener(GENERAL_SETTINGS_LISTENER); } void writeProperties(java.util.Properties p) { p.setProperty("version", "1.0"); } void readProperties(java.util.Properties p) { String version = p.getProperty("version"); } public ContextMonitorHandler createHandler(ContextMonitorDelegate delegate) { synchronized (delegateHandlers) { ContextMonitorHandler monitor = new ContextMonitorHandler(delegate); delegateHandlers.put(delegate, monitor); return monitor; } } public void refreshChildrenColor() { Iterator<Child> itr = latestChildren.iterator(); while (itr.hasNext()) { Child child = itr.next(); if (!child.isSomeHowLatest()) { model.nodeChanged(child.node); itr.remove(); } } } public void refreshChildrenColorToRemove(DefaultMutableTreeNode parent) { Iterator<Child> itr = latestChildren.iterator(); while (itr.hasNext()) { Child child = itr.next(); if (child.node.getParent().equals(parent)) { itr.remove(); } } } @Override @SuppressWarnings("unchecked") public void resultChanged(LookupEvent evt) { Lookup.Result<ContextMonitorDelegate> r = (Lookup.Result<ContextMonitorDelegate>) evt.getSource(); Collection<? extends ContextMonitorDelegate> entries = r.allInstances(); if (CollectionUtils.isNotEmpty(entries)) { lastActiveMonitor = entries.iterator().next(); } } public class ContextMonitorHandler implements IOEntryListener { private ContextMonitorDelegate delegate; private DefaultMutableTreeNode node; private Parent parent; private Map<Long, Child> childMap = new HashMap<Long, Child>(); private boolean closed = false; public ContextMonitorHandler(ContextMonitorDelegate delegate) { this.delegate = delegate; parent = new Parent(delegate); node = new DefaultMutableTreeNode(parent); node.setAllowsChildren(true); root.add(node); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { model.nodeStructureChanged(root); } }); } @Override public void started(IOEntryEvent evt) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { parent.start(); model.nodeChanged(node); } }); } @Override public void stopped(IOEntryEvent evt) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { synchronized (this) { if (!closed) { parent.stop(); model.nodeChanged(node); } } } }); } @Override public void receivedEntry(IOEntryEvent evt) { final Entry entry = evt.getEntry().getEntry(); if (RequestEntry.class.isInstance(entry)) { final Child child = new Child(delegate, (RequestEntry) entry); final DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(child); childNode.setAllowsChildren(false); child.setNode(childNode); node.add(childNode); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { child.start(); childMap.put(entry.getBaseRequestId(), child); synchronized (this) { if (!closed) { model.nodeStructureChanged(node); } } if (node.getChildCount() > 0) { jTree.expandPath(new TreePath(node.getPath())); } } }); } else if (ResponseEntry.class.isInstance(entry)) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Child child = childMap.get(entry.getBaseRequestId()); if (child != null) { child.stop(); synchronized (this) { if (!closed) { model.nodeChanged(child.node); } } } } }); } SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (btnColorLatest.isSelected()) { synchronized (latestChildren) { Child child = childMap.get(entry.getBaseRequestId()); if (child != null) { child.touch(); latestChildren.add(child); refreshChildrenColor(); } } } } }); } public void reset() { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { synchronized (this) { refreshChildrenColorToRemove(node); node.removeAllChildren(); model.nodeStructureChanged(node); } } }); } public void close() { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { synchronized (this) { refreshChildrenColorToRemove(node); root.remove(node); model.nodeStructureChanged(root); closed = true; synchronized (delegateHandlers) { delegateHandlers.remove(delegate); } } } }); } } public class Parent { private ContextMonitorDelegate delegate; private boolean alive; public Parent(ContextMonitorDelegate delegate) { this.delegate = delegate; alive = false; } public void start() { alive = true; } public void stop() { alive = false; } @Override public String toString() { return delegate.getName(); } } public final class Child { private ContextMonitorDelegate delegate; private boolean done; private DefaultMutableTreeNode node; private RequestEntry entry; private long timestamp; public Child(ContextMonitorDelegate delegate, RequestEntry entry) { this.delegate = delegate; this.entry = entry; done = false; touch(); } public void touch() { timestamp = System.currentTimeMillis(); } public boolean isLatest() { return btnColorLatest.isSelected() && System.currentTimeMillis() - timestamp <= LATEST_TIME_THRESHOLD; } public boolean isSomeHowLatest() { return btnColorLatest.isSelected() && System.currentTimeMillis() - timestamp <= SOME_HOW_LATEST_TIME_THRESHOLD; } public void setNode(DefaultMutableTreeNode node) { this.node = node; } public void start() { done = false; } public void showRequestView() { Class<? extends TopComponent> viewClass = EntryViewProviderManager.INSTANCE.getViewClass(entry); entryContent.add(entry); if (viewClass != null) { WindowUtils.selectWindow(viewClass); } } public void select() { delegate.selectRequest(entry); showRequestView(); } public void stop() { done = true; } @Override public String toString() { return entry.toString(); } } public class TreeCellRenderer extends DefaultTreeCellRenderer { public static final String STOPPED_ICON_PATH = "org/piraso/ui/base/icons/status-stopped.png"; public static final String STARTED_ICON_PATH = "org/piraso/ui/base/icons/status-active.png"; public static final String DONE_ICON_PATH = "org/piraso/ui/base/icons/tick.png"; public static final String IN_PROGRESS_ICON_PATH = "org/piraso/ui/base/icons/ui-progress-bar-icon.png"; @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { JLabel label = (JLabel) super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); if (sel) { label.setForeground(Color.WHITE); } else { label.setForeground(Color.BLACK); } DefaultMutableTreeNode node = (DefaultMutableTreeNode) value; if (Parent.class.isInstance(node.getUserObject())) { Parent parent = (Parent) node.getUserObject(); if (parent.alive) { label.setIcon(ImageUtilities.loadImageIcon(STARTED_ICON_PATH, true)); } else { label.setIcon(ImageUtilities.loadImageIcon(STOPPED_ICON_PATH, true)); } label.setFont(FontProviderManager.INSTANCE.getBoldEditorDefaultFont()); } if (Child.class.isInstance(node.getUserObject())) { Child child = (Child) node.getUserObject(); if (child.done) { label.setIcon(ImageUtilities.loadImageIcon(DONE_ICON_PATH, true)); } else { label.setIcon(ImageUtilities.loadImageIcon(IN_PROGRESS_ICON_PATH, true)); } label.setFont(FontProviderManager.INSTANCE.getEditorDefaultFont()); if (child.isLatest()) { if (sel) { label.setForeground(new Color(0xBAEEBA)); } else { label.setForeground(new Color(0x008000)); } } else if (child.isSomeHowLatest()) { if (sel) { label.setForeground(new Color(0xE2FAFF)); } else { label.setForeground(new Color(0x000080)); } } } return label; } } }