/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.openide.explorer.propertysheet;
import java.util.*;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import org.openide.ErrorManager;
import org.openide.actions.*;
import org.openide.nodes.*;
import org.openide.util.HelpCtx;
import org.openide.util.WeakListener;
/**
* A node used by PropertySheet to display common properties of
* more nodes.
* @author David Strupl
*/
final class ProxyNode extends AbstractNode {
private Node[] original;
private PropertyChangeListener pcl;
public ProxyNode(Node[] original) {
super (Children.LEAF);
this.original = original;
pcl = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent pce) {
firePropertyChange(pce.getPropertyName(), pce.getOldValue(), pce.getNewValue());
}
};
for (int i = 0; i < original.length; i++) {
original[i].addPropertyChangeListener(
WeakListener.propertyChange(pcl, original[i]));
}
}
public HelpCtx getHelpCtx () {
for (int i = 0; i < original.length; i++) {
if (original[i].getHelpCtx() != HelpCtx.DEFAULT_HELP) {
return original[i].getHelpCtx();
}
}
return HelpCtx.DEFAULT_HELP;
}
public Node cloneNode () {
return new ProxyNode(original);
}
protected Sheet createSheet () {
Sheet sheet = super.createSheet ();
Sheet.Set[] computedSet = computePropertySets();
for (int i = 0; i < computedSet.length; i++) {
sheet.put(computedSet[i]);
}
return sheet;
}
/** */
Node[] getOriginalNodes() {
return original;
}
/** Computes intersection of tabs and intersection
* of properties in those tabs.
*/
private Sheet.Set[] computePropertySets() {
if (original.length > 0) {
Node.PropertySet []firstSet = original[0].getPropertySets();
java.util.Set sheets = new HashSet(
Arrays.asList(firstSet));
// compute intersection of all Node.PropertySets for given nodes
for (int i = 1; i < original.length; i++) {
sheets.retainAll(
new HashSet(Arrays.asList(original[i].getPropertySets())));
}
ArrayList resultSheets = new ArrayList(sheets.size());
// now for all resulting sheets take common properties
for (int i = 0; i < firstSet.length; i++) {
if (! sheets.contains(firstSet[i]) || firstSet[i].isHidden ()) {
continue;
}
Node.PropertySet current = firstSet[i];
// creates an empty Sheet.Set with same names as current
Sheet.Set res = new Sheet.Set();
res.setName(current.getName());
res.setDisplayName(current.getDisplayName());
res.setShortDescription(current.getShortDescription());
java.util.Set props = new HashSet(
Arrays.asList(current.getProperties()));
// intersection of properties from the corresponding tabs
for (int j = 0; j < original.length; j++) {
Node.PropertySet[] p = original[j].getPropertySets();
for (int k = 0; k < p.length; k++) {
Node.Property[] arr = p[k].getProperties();
if (current.getName().equals(p[k].getName())) {
props.retainAll(new HashSet(
Arrays.asList(p[k].getProperties())));
}
}
}
Node.Property []p = current.getProperties();
for (int j = 0; j < p.length; j++) {
if (! props.contains(p[j])) {
continue;
}
if (p[j].isHidden ()) {
continue;
}
ProxyProperty pp = createProxyProperty(
p[j].getName(),
res.getName()
);
res.put(pp);
}
resultSheets.add(res);
}
return (Sheet.Set[])resultSheets.toArray(
new Sheet.Set[resultSheets.size()]);
}
return new Sheet.Set[0];
}
/** Finds properties in original with specified
* name in all tabs and constructs a ProxyProperty instance.
*/
private ProxyProperty createProxyProperty(String propName, String setName) {
Node.Property []arr = new Node.Property[original.length];
for (int i = 0; i < original.length; i++) {
Node.PropertySet[] p = original[i].getPropertySets();
for (int j = 0; j < p.length; j++) {
if (p[j].getName().equals(setName)) {
Node.Property[] np = p[j].getProperties();
for (int k = 0; k < np.length; k++) {
if (np[k].getName().equals(propName)) {
arr[i] = np[k];
}
}
}
}
}
return new ProxyProperty(arr);
}
/** Property delegating to an array of Properties. It either
* delegates to original[0] or applies changes to all
* original properties.
*/
private static class ProxyProperty extends Node.Property {
private Node.Property[] original;
/** It sets name, displayName and short description.
* Remembers original.
*/
public ProxyProperty(Node.Property[] original) {
super(original[0].getValueType());
this.original = original;
setName(original[0].getName());
setDisplayName(original[0].getDisplayName());
setShortDescription(original[0].getShortDescription());
}
/** Test whether the property is writable.Calls all delegates.
* If any of them returns false returns false, otherwise return true.
*/
public boolean canWrite() {
for (int i = 0; i < original.length; i++) {
if (!original[i].canWrite()) {
return false;
}
}
return true;
}
/** Test whether the property is readable. Calls all delegates.
* If any of them returns false returns false, otherwise return true.
* @return <CODE>true</CODE> if all delegates returned true
*/
public boolean canRead() {
for (int i = 0; i < original.length; i++) {
if (!original[i].canRead()) {
return false;
}
}
return true;
}
/** If all values are the same returns the value otherwise returns null.
* @return the value of the property
* @exception IllegalAccessException cannot access the called method
* @exception InvocationTargetException an exception during invocation
*/
public Object getValue() throws IllegalAccessException, java.lang.reflect.InvocationTargetException {
Object o = original[0].getValue();
if (o == null) {
return null;
}
for (int i = 0; i < original.length; i++) {
if (! o.equals(original[i].getValue())) {
throw new DifferentValuesException();
}
}
return o;
}
/** Set the value. Calls setValue on all delegates.
* @param val the new value of the property
* @exception IllegalAccessException cannot access the called method
* @exception IllegalArgumentException wrong argument
* @exception InvocationTargetException an exception during invocation
*/
public void setValue(Object val) throws IllegalAccessException, IllegalArgumentException, java.lang.reflect.InvocationTargetException {
for (int i = 0; i < original.length; i++) {
original[i].setValue(val);
}
}
/** Retrieve a named attribute with this feature.
* If all values are the same returns the value otherwise returns null.
* @param attributeName The locale-independent name of the attribute
* @return The value of the attribute. May be null if
* the attribute is unknown.
*/
public Object getValue (String attributeName) {
Object o = original[0].getValue (attributeName);
if (o == null) {
return null;
}
for (int i = 0; i < original.length; i++) {
if (! o.equals (original[i].getValue (attributeName))) {
// avoid propagate DifferentValuesException outside propertysheet package
//throw new DifferentValuesException();
// notify in log as informational and retrun null
ErrorManager.getDefault ().notify (ErrorManager.INFORMATIONAL,
new DifferentValuesException ("Different values in attribute "+attributeName)); // NOI18N
return null;
}
}
return o;
}
/** Associate a named attribute with this feature. Calls setValue on all delegates.
* @param attributeName The locale-independent name of the attribute
* @param value The value.
*/
public void setValue (String attributeName, Object value) {
for (int i = 0; i < original.length; i++) {
original[i].setValue (attributeName, value);
}
}
/**
* @returns property editor from the first delegate
*/
public java.beans.PropertyEditor getPropertyEditor () {
return original[0].getPropertyEditor();
}
/** Test whether the property has a default value. If any of
* the delegates does not support default value returns false,
* otherwise returns true.
* @return <code>true</code> if all delegates returned true
*/
public boolean supportsDefaultValue () {
for (int i = 0; i < original.length; i++) {
if (!original[i].supportsDefaultValue()) {
return false;
}
}
return true;
}
/**
* Calls restoreDefaultValue on all delegates (original).
* @exception IllegalAccessException cannot access the called method
* @exception InvocationTargetException an exception during invocation
*/
public void restoreDefaultValue() throws IllegalAccessException, java.lang.reflect.InvocationTargetException {
for (int i = 0; i < original.length; i++) {
original[i].restoreDefaultValue();
}
}
}
/** We cannot return a single value when there are different values */
static class DifferentValuesException extends RuntimeException {
public DifferentValuesException () {
super ();
}
public DifferentValuesException (String message) {
super (message);
}
}
}