/*
* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.visualvm.core.properties;
import com.sun.tools.visualvm.core.datasource.DataSource;
import com.sun.tools.visualvm.core.datasupport.Positionable;
import com.sun.tools.visualvm.core.ui.components.ScrollableContainer;
import com.sun.tools.visualvm.core.ui.components.SectionSeparator;
import com.sun.tools.visualvm.core.ui.components.Spacer;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JTabbedPane;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/**
* UI component for presenting user-customizable properties of a DataSource.
* This class is to be used by DataSource providers supporting defining the
* initial DataSource properties. Use PropertiesSupport.getCustomizer(Class)
* method to get an instance of PropertiesCustomizer for a concrete DataSource
* type.
*
* @since VisualVM 1.2
* @author Jiri Sedlacek
*/
public final class PropertiesCustomizer<X extends DataSource> extends PropertiesPanel {
private final X dataSource;
private final List<List<PropertiesProvider<X>>> groups;
private final List<PropertiesPanel> panels;
private final ChangeListener listener = new ChangeListener() {
public void stateChanged(ChangeEvent e) { update(); }
};
private JTabbedPane tabbedPane;
private final Map<Integer, Integer> categories;
PropertiesCustomizer(X dataSource, Class<X> type) {
this.dataSource = dataSource;
groups = createGroups(PropertiesSupport.sharedInstance().getProviders(dataSource, type));
panels = createPanels(groups, dataSource);
categories = new HashMap();
initComponents();
update();
registerListeners();
}
/**
* Invokes PropertiesProvider.propertiesDefined method for every
* PropertiesProvider supporting the provided DataSource. To be called by
* DataSource providers when the New DataSource dialog displaying editable
* properties has been accepted by the user and new DataSource has been
* created.
*
* @param dataSource newly created DataSource
*/
public void propertiesDefined(X dataSource) {
unregisterListeners();
for (int i = 0; i < groups.size(); i++) {
List<PropertiesProvider<X>> providers = groups.get(i);
List<PropertiesPanel> categoriesPanels = new ArrayList();
if (providers.size() == 1) {
categoriesPanels.add(panels.get(i));
} else {
MultiPropertiesPanel multiPanel = (MultiPropertiesPanel)panels.get(i);
categoriesPanels.addAll(multiPanel.getPanels());
}
for (int j = 0; j < providers.size(); j++)
providers.get(j).propertiesDefined(categoriesPanels.get(j), dataSource);
}
}
/**
* Invokes PropertiesProvider.propertiesCancelled method for every
* PropertiesProvider supporting the DataSource type defined for this
* PropertiesCustomizer. To be called by DataSource providers when the New
* DataSource dialog displaying editable properites has been cancelled and
* no DataSource has been created.
*/
public void propertiesCancelled() {
unregisterListeners();
for (int i = 0; i < groups.size(); i++) {
List<PropertiesProvider<X>> providers = groups.get(i);
List<PropertiesPanel> categoriesPanels = new ArrayList();
if (providers.size() == 1) {
categoriesPanels.add(panels.get(i));
} else {
MultiPropertiesPanel multiPanel = (MultiPropertiesPanel)panels.get(i);
categoriesPanels.addAll(multiPanel.getPanels());
}
for (int j = 0; j < providers.size(); j++)
providers.get(j).propertiesCancelled(categoriesPanels.get(j), dataSource);
}
}
void propertiesChanged() {
unregisterListeners();
for (int i = 0; i < groups.size(); i++) {
List<PropertiesProvider<X>> providers = groups.get(i);
List<PropertiesPanel> categoriesPanels = new ArrayList();
if (providers.size() == 1) {
categoriesPanels.add(panels.get(i));
} else {
MultiPropertiesPanel multiPanel = (MultiPropertiesPanel)panels.get(i);
categoriesPanels.addAll(multiPanel.getPanels());
}
for (int j = 0; j < providers.size(); j++)
providers.get(j).propertiesChanged(categoriesPanels.get(j), dataSource);
}
}
void selectCategory(int category) {
Integer tabIndex = categories.get(category);
if (tabIndex != null) tabbedPane.setSelectedIndex(tabIndex);
}
private void registerListeners() {
for (PropertiesPanel panel : panels)
panel.addChangeListener(listener);
}
private void unregisterListeners() {
for (PropertiesPanel panel : panels)
panel.removeChangeListener(listener);
}
private void update() {
for (PropertiesPanel panel : panels)
if (!panel.settingsValid()) {
setSettingsValid(false);
return;
}
setSettingsValid(true);
}
private void initComponents() {
tabbedPane = new JTabbedPane();
tabbedPane.setFocusable(false);
for (int i = 0; i < panels.size(); i++) {
PropertiesPanel panel = panels.get(i);
PropertiesProvider provider = groups.get(i).get(0);
ScrollableContainer c = new ScrollableContainer(panel);
c.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
c.setViewportBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
categories.put(provider.getPropertiesCategory(), tabbedPane.getTabCount());
tabbedPane.addTab(provider.getPropertiesName(), null, c,
provider.getPropertiesDescription());
}
setLayout(new BorderLayout());
add(tabbedPane, BorderLayout.CENTER);
}
private static <Y extends DataSource> List<List<PropertiesProvider<Y>>>
createGroups(List<PropertiesProvider<Y>> providers) {
Collections.sort(providers, new CategoriesComparator());
List<List<PropertiesProvider<Y>>> groupedProviders = new ArrayList();
int currentCategory = -1;
List<PropertiesProvider<Y>> currentGroup = null;
for (PropertiesProvider<Y> provider : providers) {
int providerCategory = provider.getPropertiesCategory();
if (currentGroup == null || providerCategory != currentCategory) {
currentCategory = providerCategory;
if (currentGroup != null)
Collections.sort(currentGroup, Positionable.COMPARATOR);
currentGroup = new ArrayList();
groupedProviders.add(currentGroup);
}
if (currentGroup != null && currentCategory == providerCategory)
currentGroup.add(provider);
}
if (currentGroup != null)
Collections.sort(currentGroup, Positionable.COMPARATOR);
return groupedProviders;
}
private static <Y extends DataSource> List<PropertiesPanel>
createPanels(List<List<PropertiesProvider<Y>>> groups, Y dataSource) {
List<PropertiesPanel> panels = new ArrayList(groups.size());
for (List<PropertiesProvider<Y>> group : groups)
if (group.size() == 1)
panels.add(group.get(0).createPanel(dataSource));
else
panels.add(new MultiPropertiesPanel(group, dataSource));
return panels;
}
private static class MultiPropertiesPanel<Y extends DataSource> extends PropertiesPanel {
private static final Color separatorColor = separatorColor();
private static final Font separatorFont = separatorFont();
private final List<PropertiesPanel> panels;
private final ChangeListener listener = new ChangeListener() {
public void stateChanged(ChangeEvent e) { update(); }
};
public MultiPropertiesPanel(List<PropertiesProvider<Y>> providers, Y dataSource) {
panels = new ArrayList(providers.size());
for (PropertiesProvider provider : providers)
panels.add(provider.createPanel(dataSource));
initComponents(providers);
update();
}
public List<PropertiesPanel> getPanels() {
return panels;
}
private void update() {
boolean valid = true;
for (PropertiesPanel panel : panels)
if (!panel.settingsValid()) {
valid = false;
break;
}
setSettingsValid(valid);
}
private void initComponents(List<PropertiesProvider<Y>> providers) {
setOpaque(false);
setBorder(BorderFactory.createEmptyBorder());
setLayout(new GridBagLayout());
int currentRow = 0;
int providerIndex = 1;
GridBagConstraints constraints;
for (PropertiesPanel panel : panels) {
constraints = createConstraints(currentRow++);
add(panel, constraints);
panel.addChangeListener(listener);
if (providerIndex < providers.size()) {
PropertiesProvider provider = providers.get(providerIndex++);
SectionSeparator separator = new SectionSeparator(provider.getPropertiesName());
separator.setForeground(separatorColor);
separator.setFont(separatorFont);
separator.setToolTipText(provider.getPropertiesDescription());
constraints = createConstraints(currentRow++);
constraints.insets = new Insets(10, 0, 5, 0);
add(separator, constraints);
}
}
constraints = createConstraints(currentRow++);
constraints.weightx = 1;
constraints.weighty = 1;
add(Spacer.create(), constraints);
}
private static GridBagConstraints createConstraints(int row) {
GridBagConstraints c = new GridBagConstraints();
c.gridx = 0;
c.gridy = row;
c.gridwidth = GridBagConstraints.REMAINDER;
c.anchor = GridBagConstraints.NORTHWEST;
c.fill = GridBagConstraints.BOTH;
return c;
}
}
private static Color separatorColor() {
Color color = UIManager.getColor("TitledBorder.titleColor"); // NOI18N
if (color == null) color = new JLabel().getForeground();
return color;
}
private static Font separatorFont() {
Font font = UIManager.getFont("TitledBorder.font"); // NOI18N
if (font == null) font = new JLabel().getFont();
return font;
}
private static class CategoriesComparator implements Comparator {
public int compare(Object o1, Object o2) {
PropertiesProvider p1 = (PropertiesProvider)o1;
PropertiesProvider p2 = (PropertiesProvider)o2;
int category1 = p1.getPropertiesCategory();
int category2 = p2.getPropertiesCategory();
if (category1 > category2) return 1;
if (category1 < category2) return -1;
return p1.getPropertiesName().compareTo(p2.getPropertiesName());
}
}
}