/*
* 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.datasource;
import com.sun.tools.visualvm.core.datasupport.DataRemovedListener;
import com.sun.tools.visualvm.core.datasupport.ComparableWeakReference;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.openide.util.RequestProcessor;
/**
* Abstract implementation of DataSource.
* DataSource is a base element of all data sources in VisualVM like applications,
* hosts, thread dumps etc.
*
* @author Jiri Sedlacek
*/
public abstract class DataSource {
/**
* Named property for DataSource visibility.
*/
public static final String PROPERTY_VISIBLE = "prop_visible"; // NOI18N
/**
* Event dispatch thread for all DataSource events. All operations on DataSources should be invoked
* in this thread - similar to UI operations performed in AWT event dispatch thread.
*/
public static final RequestProcessor EVENT_QUEUE = new RequestProcessor("DataSource Event Queue"); // NOI18N
/**
* Virtual root of DataSource tree.
* ROOT corresponds to invisible root of explorer tree.
*/
public static final DataSource ROOT = new DataSource() {};
private DataSource owner;
private boolean isRemoved = false;
private DataSource master;
private boolean visible = true;
private Storage storage;
private DataSourceContainer repository;
private PropertyChangeSupport changeSupport;
private Set<ComparableWeakReference<DataRemovedListener>> removedListeners;
/**
* Creates new instance of DataSource.
*/
public DataSource() {
this(null);
}
/**
* Creates new instance of DataSource with defined master.
*
* @param master master of the DataSource.
*/
public DataSource(DataSource master) {
this.master = master;
}
/**
* Returns owner (parent) DataSource of this DataSource.
* @return owner (parent) DataSource of this DataSource.
*/
public final DataSource getOwner() {
return owner;
}
/**
* Sets visibility of the DataSource.
*
* @param newVisible visibility of the DataSource.
*/
public final synchronized void setVisible(boolean newVisible) {
if (this == DataSource.ROOT && !newVisible) throw new IllegalArgumentException("DataSourceRoot cannot be hidden"); // NOI18N
boolean oldVisible = visible;
visible = newVisible;
getChangeSupport().firePropertyChange(PROPERTY_VISIBLE, oldVisible, newVisible);
}
/**
* Returns true if the DataSource is visible, false otherwise.
*
* @return true if the DataSource is visible, false otherwise.
*/
public final boolean isVisible() {
return visible;
}
/**
* Returns master of the DataSource.
*
* @return master of the DataSource.
*/
public final DataSource getMaster() {
return master;
}
/**
* Returns storage for the DataSource.
*
* @return storage for the DataSource.
*/
public final synchronized Storage getStorage() {
if (storage == null) {
storage = createStorage();
if (storage == null) throw new NullPointerException("Storage cannot be null"); // NOI18N
}
return storage;
}
/**
* Returns repository of the DataSource.
* Repository is a container for other DataSources, virtually building a tree
* structure of DataSources.
*
* @return repository of the DataSource.
*/
public final synchronized DataSourceContainer getRepository() {
if (repository == null) repository = new DataSourceContainer(this);
return repository;
}
/**
* Add a PropertyChangeListener to the listener list.
* The listener is registered for all properties.
* The same listener object may be added more than once, and will be called
* as many times as it is added.
* If <code>listener</code> is null, no exception is thrown and no action
* is taken.
*
* @param listener The PropertyChangeListener to be added
*/
public final void addPropertyChangeListener(PropertyChangeListener listener) {
getChangeSupport().addPropertyChangeListener(listener);
}
/**
* Add a PropertyChangeListener for a specific property. The listener
* will be invoked only when a call on firePropertyChange names that
* specific property.
* The same listener object may be added more than once. For each
* property, the listener will be invoked the number of times it was added
* for that property.
* If <code>propertyName</code> or <code>listener</code> is null, no
* exception is thrown and no action is taken.
*
* @param propertyName The name of the property to listen on.
* @param listener The PropertyChangeListener to be added
*/
public final void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
getChangeSupport().addPropertyChangeListener(propertyName, listener);
}
/**
* Remove a PropertyChangeListener from the listener list.
* This removes a PropertyChangeListener that was registered
* for all properties.
* If <code>listener</code> was added more than once to the same event
* source, it will be notified one less time after being removed.
* If <code>listener</code> is null, or was never added, no exception is
* thrown and no action is taken.
*
* @param listener The PropertyChangeListener to be removed
*/
public final void removePropertyChangeListener(PropertyChangeListener listener) {
getChangeSupport().removePropertyChangeListener(listener);
}
/**
* Remove a PropertyChangeListener for a specific property.
* If <code>listener</code> was added more than once to the same event
* source for the specified property, it will be notified one less time
* after being removed.
* If <code>propertyName</code> is null, no exception is thrown and no
* action is taken.
* If <code>listener</code> is null, or was never added for the specified
* property, no exception is thrown and no action is taken.
*
* @param propertyName The name of the property that was listened on.
* @param listener The PropertyChangeListener to be removed
*/
public final void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
getChangeSupport().removePropertyChangeListener(propertyName, listener);
}
/**
* Returns true if the DataSource can be removed using Remove action, false otherwise.
*
* @return true if the DataSource can be removed using Remove action, false otherwise.
*/
public boolean supportsUserRemove() {
return false;
}
/**
* Adds a DataRemovedListener to be notified when the DataSource is removed from the tree.
* Note that this listener cannot be explicitely unregistered, it's weakly referenced and will
* be notified up to once and then unregistered automatically.
*
* @param listener DataRemovedListener to be notified when the DataSource is removed from the tree.
*/
public final void notifyWhenRemoved(DataRemovedListener listener) {
if (listener == null) throw new IllegalArgumentException("Listener cannot be null"); // NOI18N
if (isRemoved()) listener.dataRemoved(this);
else getRemovedListeners().add(new ComparableWeakReference(listener));
}
/**
* Returns true if the DataSource has already been removed from DataSources tree, false otherwise.
* @return true if the DataSource has already been removed from DataSources tree, false otherwise.
*/
public final boolean isRemoved() {
return isRemoved;
}
/**
* Returns true if the DataSource can be removed in context of removeRoot.
* The check is blocking, this is a chance for example to warn the user about
* possible data loss when removing the DataSource representing an unsaved snapshot.
*
* @param removeRoot DataSource which invoked the removal action (topmost DataSource to be removed).
* @return true if the DataSource can be removed in context of removeRoot, false otherwise.
*/
public boolean checkRemove(DataSource removeRoot) {
return true;
}
// Implementation of this DataSource removal
// Persistent DataSources can remove appropriate entries from their storage
protected void remove() {
getStorage().deleteCustomPropertiesStorage();
}
final void addImpl(DataSource owner) {
if (isRemoved) throw new UnsupportedOperationException("DataSource can be added only once"); // NOI18N
this.owner = owner;
}
final void removeImpl() {
remove();
this.owner = null;
isRemoved = true;
if (!hasRemovedListeners()) return;
Set<ComparableWeakReference<DataRemovedListener>> listeners = getRemovedListeners();
for (WeakReference<DataRemovedListener> listenerReference : listeners) {
DataRemovedListener listener = listenerReference.get();
if (listener != null) listener.dataRemoved(this);
}
listeners.clear();
}
/**
* Creates Storage instance for this DataSource.
* This method should never return null.
*
* @return Storage instance for this DataSource.
*/
protected Storage createStorage() {
return new Storage();
}
/**
* Returns instance of PropertyChangeSupport used for processing property changes.
*
* @return instance of PropertyChangeSupport used for processing property changes.
*/
protected final synchronized PropertyChangeSupport getChangeSupport() {
if (changeSupport == null) changeSupport = new PropertyChangeSupport(this);
return changeSupport;
}
final boolean hasRemovedListeners() {
return removedListeners != null;
}
final synchronized Set<ComparableWeakReference<DataRemovedListener>> getRemovedListeners() {
if (!hasRemovedListeners()) removedListeners = Collections.synchronizedSet(new HashSet());
return removedListeners;
}
}