/** * This file Copyright (c) 2005-2008 Aptana, Inc. This program is * dual-licensed under both the Aptana Public License and the GNU General * Public license. You may elect to use one or the other of these licenses. * * This program is distributed in the hope that it will be useful, but * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or * NONINFRINGEMENT. Redistribution, except as permitted by whichever of * the GPL or APL you select, is prohibited. * * 1. For the GPL license (GPL), you can redistribute and/or modify this * program under the terms of the GNU General Public License, * Version 3, as published by the Free Software Foundation. You should * have received a copy of the GNU General Public License, Version 3 along * with this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Aptana provides a special exception to allow redistribution of this file * with certain other free and open source software ("FOSS") code and certain additional terms * pursuant to Section 7 of the GPL. You may view the exception and these * terms on the web at http://www.aptana.com/legal/gpl/. * * 2. For the Aptana Public License (APL), this program and the * accompanying materials are made available under the terms of the APL * v1.0 which accompanies this distribution, and is available at * http://www.aptana.com/legal/apl/. * * You may view the GPL, Aptana's exception and additional terms, and the * APL in the file titled license.html at the root of the corresponding * plugin containing this source file. * * Any modifications to this file must keep this entire header intact. */ package com.aptana.ide.editors.views.outline.propertyManager; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.Serializable; import java.util.ArrayList; import java.util.Hashtable; import java.util.Iterator; import org.eclipse.ui.views.properties.IPropertyDescriptor; import org.eclipse.ui.views.properties.IPropertySource; import org.eclipse.ui.views.properties.IPropertySourceProvider; import org.eclipse.ui.views.properties.PropertyDescriptor; import com.aptana.ide.core.StringUtils; /** * This class is designed to make integration with the propery view easier. ReadableProperties are added to the property * manager, which handles the interaction between the property view and the actual properties. * * @author Dan Phifer June 8, 2005 */ public class PropertyManager implements PropertyChangeListener, IPropertySource, IPropertySourceProvider, Serializable { /** * */ private static final long serialVersionUID = 1L; /** * Contains the references to the properties that have been added to this */ protected ArrayList properties; /** * Contains a list of property descriptors that have been previously created and can be reused. Dynamic descriptors * are not cached. * * @see ReadableProperty#hasDynamicPropertyDescriptor() */ private transient Hashtable knownDescriptors; /** * A list of property change listeners */ private transient ArrayList propertyChangeListeners; /** * The object to which the properties belong. All events are fired on belhalf of the owner. * * @see PropertyManager#notifyPropertyChangeListeners(PropertyChangeEvent) */ protected Object owner; /** * Creates a new instance of the PropertyManager class. * * @param owner * The object to which the properties belong. */ public PropertyManager(Object owner) { this.owner = owner; properties = new ArrayList(); knownDescriptors = new Hashtable(); linkPropertyOwner(owner); } /** * If the owner of this property is another property, link them together * * @param owner */ private void linkPropertyOwner(Object owner) { if (owner instanceof ReadableProperty) { ReadableProperty property = (ReadableProperty) owner; property.setSubPropertyManager(this); } } /** * Adds a ReadableProperty to the PropertyManager. If a property with the same ID already exists, this method has no * effect. * * @param readableProperty * An ReadableProperty */ public void addProperty(ReadableProperty readableProperty) { if (!hasProperty(readableProperty.getID())) { readableProperty.addPropertyChangeListener(this); properties.add(readableProperty); } } /** * Removes an ReadableProperties from the PropertyManager * * @param readableProperty * An ReadableProperty */ public void removeProperty(ReadableProperty readableProperty) { readableProperty.removePropertyChangeListener(this); properties.remove(readableProperty); } /** * Adds a property change listener to be notified when any of the managed ReadableProperties are modifed. * * @param listener * A PropertyChangeListener */ public void addPropertyChangeListener(PropertyChangeListener listener) { getPropertyChangeListeners().add(listener); } /** * Removes the given property change listener. * * @param listener * a property listener */ public void removePropertyChangeListener(PropertyChangeListener listener) { getPropertyChangeListeners().remove(listener); } /** * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent) */ public void propertyChange(PropertyChangeEvent event) { notifyPropertyChangeListeners(event); } /** * Notify listeners that a property has changed and pass the event * * @param event * The event describing the property change */ private void notifyPropertyChangeListeners(PropertyChangeEvent event) { // fire an event on behalf of the property manager owner PropertyChangeEvent newEvent = new PropertyChangeEvent(owner, event.getPropertyName(), event.getOldValue(), event.getNewValue()); for (Iterator iter = getPropertyChangeListeners().iterator(); iter.hasNext();) { PropertyChangeListener listener = (PropertyChangeListener) iter.next(); listener.propertyChange(newEvent); } } /** * @param id * @return The EditableProperty that corresponds to the given ID */ public EditableProperty getEditableProperty(Object id) { ReadableProperty prop = getProperty(id); if (prop != null) { if (prop instanceof EditableProperty) { return (EditableProperty) prop; } } return null; } /** * @param id * The ID of the property to be returned * @return The EditableProperty that corresponds the the given ID * @throws IllegalArgumentException * if a property with the given ID has not been added to the PropertyManager */ public ReadableProperty getProperty(Object id) { ReadableProperty prop = findProperty(id); // if the property was found, return it, otherwise throw an error if (prop != null) { return prop; } String error = StringUtils.format(Messages.PropertyManager_CouldNotFindProperty,id); throw new IllegalArgumentException(error); } /** * @param id * The ID of a property * @return true if the property exists in the PropertyManager class */ public boolean hasProperty(Object id) { return findProperty(id) != null; } /** * This method simply calls getProperty and is provided for consistency * * @param id * The ID of a property * @return The corresponding property */ public ReadableProperty getReadableProperty(Object id) { return getProperty(id); } private ReadableProperty findProperty(Object id) { for (Iterator iter = properties.iterator(); iter.hasNext();) { ReadableProperty prop = (ReadableProperty) iter.next(); if (prop.getID().equals(id)) { return prop; } } return null; } /** * Creates an array of property descriptors which will be used by the properties view. One property descriptor is * created for each EditableProperty managed by the PropertyManager * * @return an array of property descriptors for use with the property view. */ public IPropertyDescriptor[] getPropertyDescriptors() { IPropertyDescriptor[] descriptors = new PropertyDescriptor[properties.size()]; int p = 0; for (Iterator iter = properties.iterator(); iter.hasNext();) { ReadableProperty readableProperty = (ReadableProperty) iter.next(); if (readableProperty != null) { // if the descriptor is dynamic, do not cache the output if (readableProperty.hasDynamicPropertyDescriptor()) { descriptors[p] = readableProperty.getPropertyDescriptor(); } // if it is not dynamic, cache the output and reuse the descriptor // if it is already available else { String id = readableProperty.getID(); if (!getKnownDescriptors().containsKey(id)) { getKnownDescriptors().put(id, readableProperty.getPropertyDescriptor()); } descriptors[p] = (IPropertyDescriptor) getKnownDescriptors().get(id); } p++; } } return descriptors; } /** * @param id * The id of the EditableProperty * @return the value for the given property. This value should be valid for the properties view. Subclasses of * EditableProperty should provide other methods for returning the value in a different form. For instance, * <code>EditableComboProperty</code> will return an Integer which represents the index of the selected * item in the combo box. However, the correspoding value (which is ultimately what is wanted by the class * that is using the <code>EditableComboProperty</code>) is returned by getComboValue(). */ public Object getPropertyValue(Object id) { ReadableProperty readableProperty = getProperty(id); if (readableProperty != null) { if (readableProperty.hasSubProperties()) { return readableProperty.getSubPropertyManager(); } else { return readableProperty.getUnderlyingValue(); } } return null; } /** * @param id * The id of the property * @return A Number if the given property implements INumericProperty * @throws IllegalArgumentException * if the property does not exist or does not implement INumericProperty */ public Number getPropertyNumberValue(Object id) { ReadableProperty readableProperty = getProperty(id); if (readableProperty instanceof INumericProperty) { return ((INumericProperty) readableProperty).getNumberValue(); } else { throw new IllegalArgumentException(StringUtils.format(Messages.PropertyManager_NotNumericProperty, id)); } } /** * @param id * The id of the property * @return An int if the given property implements INumericProperty * @throws IllegalArgumentException * if the property does not exist or does not implement INumericProperty */ public int getPropertyIntValue(Object id) throws IllegalArgumentException { return getPropertyNumberValue(id).intValue(); } /** * @param id * The id of the property * @return A float if the given property implements INumericProperty * @throws IllegalArgumentException * if the property does not exist or does not implement INumericProperty */ public float getPropertyFloatValue(Object id) throws IllegalArgumentException { return getPropertyNumberValue(id).floatValue(); } /** * @param id * The id of the property * @return A double if the given property implements INumericProperty * @throws IllegalArgumentException * if the property does not exist or does not implement INumericProperty */ public double getPropertyDoubleValue(Object id) throws IllegalArgumentException { return getPropertyNumberValue(id).doubleValue(); } /** * @param id * The id of the property * @return A long if the given property implements INumericProperty * @throws IllegalArgumentException * if the property does not exist or does not implement INumericProperty */ public long getPropertyLongValue(Object id) throws IllegalArgumentException { return getPropertyNumberValue(id).longValue(); } /** * @param id * The id of the EditableProperty * @return true if the property is anything other than the defaultValue */ public boolean isPropertySet(Object id) { ReadableProperty readableProperty = getProperty(id); if (readableProperty != null) { if (readableProperty.hasDefaultValue() && readableProperty.hasValue()) { return !readableProperty.getUnderlyingDefaultValue().equals(readableProperty.getUnderlyingValue()); } } return false; } /** * Resets the property value to the defaultValue. If not default value was specified, this method has not effect. * * @param id * The id of the EditableProperty */ public void resetPropertyValue(Object id) { EditableProperty editableProperty = getEditableProperty(id); if (editableProperty != null) { if (editableProperty.hasDefaultValue()) { editableProperty.setUnderlyingValue(editableProperty.getUnderlyingDefaultValue()); } } } /** * Sets the value of the given property * * @param id * The id of the ReadableProperty * @param value * The new value */ public void setPropertyValue(Object id, Object value) { ReadableProperty readableProperty = getProperty(id); if (readableProperty != null) { readableProperty.setUnderlyingValue(value); } } /** * @see org.eclipse.ui.views.properties.IPropertySource#getEditableValue() */ public Object getEditableValue() { if (owner instanceof ReadableProperty) { ReadableProperty property = (ReadableProperty) owner; return property.getUnderlyingValue(); } return owner; } /** * @see org.eclipse.ui.views.properties.IPropertySourceProvider#getPropertySource(java.lang.Object) */ public IPropertySource getPropertySource(Object object) { return this; } /** * @return A List with a reference to all properties managed by the property manager */ public ArrayList getProperties() { return new ArrayList(properties); } /** * @return Returns the propertyChangeListeners. If the object has not been initialized, it is initialized and * returned */ protected ArrayList getPropertyChangeListeners() { if (propertyChangeListeners == null) { propertyChangeListeners = new ArrayList(); } return propertyChangeListeners; } /** * @return Returns the knownDescriptors. If the object has not been initialized, it is initialized and returned */ protected Hashtable getKnownDescriptors() { if (knownDescriptors == null) { knownDescriptors = new Hashtable(); } return knownDescriptors; } }