I thought about that, and maybe we could. It seemed kinda heavy for most bean dictionary use cases though. We'd also need to make BeanDictionary have a source property and fire source changed events as well (since the caller will leak memory without calling BeanMonitor.setSource(null).
I'm not averse to integrating the two, but I feel like we might want to keep BeanDictionary lighter than BeanMonitor. Other than that, I'm very happy with how BeanMonitor came out. -T On Mon, Oct 26, 2009 at 9:53 AM, Greg Brown <[email protected]> wrote: > Could we not put this functionality in BeanDictionary itself? > > > On Oct 26, 2009, at 9:48 AM, [email protected] wrote: > > Author: tvolkert >> Date: Mon Oct 26 13:48:09 2009 >> New Revision: 829797 >> >> URL: http://svn.apache.org/viewvc?rev=829797&view=rev >> Log: >> Added BeanMonitor, changed EventLogger tool to use it, hooked up events to >> component inspector tool >> >> Added: >> incubator/pivot/trunk/core/src/org/apache/pivot/beans/BeanMonitor.java >> >> incubator/pivot/trunk/core/src/org/apache/pivot/beans/BeanMonitorListener.java >> Modified: >> >> incubator/pivot/trunk/tools/src/org/apache/pivot/tools/wtk/ComponentInspectorSkin.java >> >> incubator/pivot/trunk/tools/src/org/apache/pivot/tools/wtk/EventLogger.java >> >> incubator/pivot/trunk/tools/src/org/apache/pivot/tools/wtk/component_inspector_skin.wtkx >> >> Added: >> incubator/pivot/trunk/core/src/org/apache/pivot/beans/BeanMonitor.java >> URL: >> http://svn.apache.org/viewvc/incubator/pivot/trunk/core/src/org/apache/pivot/beans/BeanMonitor.java?rev=829797&view=auto >> >> ============================================================================== >> --- incubator/pivot/trunk/core/src/org/apache/pivot/beans/BeanMonitor.java >> (added) >> +++ incubator/pivot/trunk/core/src/org/apache/pivot/beans/BeanMonitor.java >> Mon Oct 26 13:48:09 2009 >> @@ -0,0 +1,364 @@ >> +/* >> + * Licensed to the Apache Software Foundation (ASF) under one or more >> + * contributor license agreements. See the NOTICE file distributed with >> + * this work for additional information regarding copyright ownership. >> + * The ASF licenses this file to you 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.apache.pivot.beans; >> + >> +import java.lang.reflect.InvocationHandler; >> +import java.lang.reflect.InvocationTargetException; >> +import java.lang.reflect.Method; >> +import java.lang.reflect.Modifier; >> +import java.lang.reflect.ParameterizedType; >> +import java.lang.reflect.Proxy; >> +import java.lang.reflect.Type; >> +import java.util.Comparator; >> + >> +import org.apache.pivot.collections.ArrayList; >> +import org.apache.pivot.collections.HashMap; >> +import org.apache.pivot.collections.Sequence; >> +import org.apache.pivot.collections.immutable.ImmutableList; >> +import org.apache.pivot.util.ListenerList; >> +import org.apache.pivot.util.ThreadUtilities; >> +import org.apache.pivot.util.Vote; >> + >> +/** >> + * Notifies listeners of events fired from a source bean. >> + */ >> +public class BeanMonitor { >> + private static class EventComparator implements Comparator<Method> { >> + @Override >> + public int compare(Method event1, Method event2) { >> + int result = 0; >> + >> + Class<?> listenerInterface1 = event1.getDeclaringClass(); >> + Class<?> listenerInterface2 = event2.getDeclaringClass(); >> + >> + if (listenerInterface1 != listenerInterface2) { >> + result = >> listenerInterface1.getName().compareTo(listenerInterface2.getName()); >> + } >> + >> + if (result == 0) { >> + result = event1.getName().compareTo(event2.getName()); >> + } >> + >> + return result; >> + } >> + } >> + >> + private static class PropertyNameComparator implements >> Comparator<String> { >> + @Override >> + public int compare(String propertyName1, String propertyName2) { >> + return propertyName1.compareTo(propertyName2); >> + } >> + } >> + >> + private class MonitorInvocationHandler implements InvocationHandler { >> + @Override >> + public Object invoke(Object proxy, Method event, Object[] >> arguments) throws Throwable { >> + beanMonitorListeners.eventFired(BeanMonitor.this, event, >> arguments); >> + >> + String eventName = event.getName(); >> + if (eventName.endsWith(PROPERTY_CHANGE_SUFFIX)) { >> + String propertyName = eventName.substring(0, >> eventName.length() >> + - PROPERTY_CHANGE_SUFFIX.length()); >> + >> + if (notifyingProperties.indexOf(propertyName) >= 0) { >> + >> beanMonitorListeners.propertyChanged(BeanMonitor.this, propertyName); >> + } >> + } >> + >> + Object result = null; >> + Class<?> returnType = event.getReturnType(); >> + if (returnType == Vote.class) { >> + result = Vote.APPROVE; >> + } else if (returnType == Boolean.TYPE) { >> + result = false; >> + } >> + >> + return result; >> + } >> + } >> + >> + private static class BeanMonitorListenerList extends >> ListenerList<BeanMonitorListener> >> + implements BeanMonitorListener { >> + @Override >> + public void sourceChanged(BeanMonitor beanMonitor, Object >> previousSource) { >> + for (BeanMonitorListener listener : this) { >> + listener.sourceChanged(beanMonitor, previousSource); >> + } >> + } >> + >> + @Override >> + public void eventFired(BeanMonitor beanMonitor, Method event, >> Object[] arguments) { >> + for (BeanMonitorListener listener : this) { >> + listener.eventFired(beanMonitor, event, arguments); >> + } >> + } >> + >> + @Override >> + public void propertyChanged(BeanMonitor beanMonitor, String >> propertyName) { >> + for (BeanMonitorListener listener : this) { >> + listener.propertyChanged(beanMonitor, propertyName); >> + } >> + } >> + } >> + >> + private Object source = null; >> + >> + private HashMap<Class<?>, Object> eventListenerProxies = new >> HashMap<Class<?>, Object>(); >> + private MonitorInvocationHandler monitorInvocationHandler = new >> MonitorInvocationHandler(); >> + >> + private ArrayList<Method> declaredEvents = new >> ArrayList<Method>(eventComparator); >> + private ArrayList<String> notifyingProperties = new >> ArrayList<String>(propertyNameComparator); >> + >> + private BeanMonitorListenerList beanMonitorListeners = new >> BeanMonitorListenerList(); >> + >> + private static EventComparator eventComparator = new >> EventComparator(); >> + private static PropertyNameComparator propertyNameComparator = new >> PropertyNameComparator(); >> + >> + private static final String PROPERTY_CHANGE_SUFFIX = "Changed"; >> + >> + /** >> + * Creates a new bean monitor that is initially associated with no >> source >> + * object. >> + */ >> + public BeanMonitor() { >> + this(null); >> + } >> + >> + /** >> + * Creates a new bean monitor that will monitor the specified source >> + * object. >> + * <p> >> + * <b>NOTE</b>: failing to clear the source of a bean monitor may >> result in >> + * memory leaks, as the source object will maintain references to the >> bean >> + * monitor as long as the source is set. >> + */ >> + public BeanMonitor(Object source) { >> + setSource(source); >> + } >> + >> + /** >> + * Gets the source of the bean monitor. >> + * >> + * @return >> + * The source object, or <tt>null</tt> if no source has been set. >> + */ >> + public Object getSource() { >> + return source; >> + } >> + >> + /** >> + * Sets the source of the bean monitor. >> + * <p> >> + * <b>NOTE</b>: failing to clear the source of a bean monitor may >> result in >> + * memory leaks, as the source object will maintain references to the >> bean >> + * monitor as long as the source is set. >> + * >> + * @param source >> + * The source object, or <tt>null</tt> to clear the source. >> + */ >> + public void setSource(Object source) { >> + Object previousSource = this.source; >> + >> + if (source != previousSource) { >> + this.source = source; >> + >> + if (previousSource != null) { >> + unregisterEventListeners(previousSource); >> + } >> + >> + declaredEvents.clear(); >> + notifyingProperties.clear(); >> + >> + if (source != null) { >> + registerEventListeners(source); >> + } >> + >> + beanMonitorListeners.sourceChanged(this, previousSource); >> + } >> + } >> + >> + /** >> + * Gets the list of events that the source bean may fire. >> + * >> + * @return >> + * The event listener methods that the source bean may invoke. >> + */ >> + public Sequence<Method> getDeclaredEvents() { >> + return new ImmutableList<Method>(declaredEvents); >> + } >> + >> + /** >> + * Gets the list of source bean property names for which property >> change >> + * events will be fired. >> + * >> + * @return >> + * The property names that fire change events. >> + */ >> + public Sequence<String> getNotifyingProperties() { >> + return new ImmutableList<String>(notifyingProperties); >> + } >> + >> + /** >> + * Tells whether or not the specified property fires change events. >> + * >> + * @return >> + * <tt>true</tt> if the property fires change events; <tt>false</tt> >> + * otherwise. >> + */ >> + public boolean isNotifyingProperty(String propertyName) { >> + return (notifyingProperties.indexOf(propertyName) >= 0); >> + } >> + >> + /** >> + * Registers event listeners on a bean. >> + */ >> + private void registerEventListeners(Object bean) { >> + BeanDictionary beanDictionary = new BeanDictionary(bean); >> + Method[] methods = bean.getClass().getMethods(); >> + >> + for (int i = 0; i < methods.length; i++) { >> + Method method = methods[i]; >> + >> + if >> (ListenerList.class.isAssignableFrom(method.getReturnType()) >> + && (method.getModifiers() & Modifier.STATIC) == 0) { >> + ParameterizedType genericType = >> (ParameterizedType)method.getGenericReturnType(); >> + Type[] typeArguments = >> genericType.getActualTypeArguments(); >> + >> + if (typeArguments.length == 1) { >> + Class<?> listenerInterface = >> (Class<?>)typeArguments[0]; >> + >> + if (!listenerInterface.isInterface()) { >> + throw new >> RuntimeException(listenerInterface.getName() >> + + " is not an interface."); >> + } >> + >> + Method[] interfaceMethods = >> listenerInterface.getMethods(); >> + for (int j = 0; j < interfaceMethods.length; j++) { >> + Method interfaceMethod = interfaceMethods[j]; >> + String interfaceMethodName = >> interfaceMethod.getName(); >> + >> + declaredEvents.add(interfaceMethod); >> + >> + if >> (interfaceMethodName.endsWith(PROPERTY_CHANGE_SUFFIX)) { >> + String propertyName = >> interfaceMethodName.substring(0, >> + interfaceMethodName.length() - >> PROPERTY_CHANGE_SUFFIX.length()); >> + >> + if (beanDictionary.containsKey(propertyName)) >> { >> + notifyingProperties.add(propertyName); >> + } >> + } >> + } >> + >> + // Get the listener list >> + Object listenerList; >> + try { >> + listenerList = method.invoke(bean); >> + } catch (InvocationTargetException exception) { >> + throw new RuntimeException(exception); >> + } catch (IllegalAccessException exception) { >> + throw new RuntimeException(exception); >> + } >> + >> + // Get the listener for this interface >> + Object listener = >> eventListenerProxies.get(listenerInterface); >> + if (listener == null) { >> + listener = >> Proxy.newProxyInstance(ThreadUtilities.getClassLoader(), >> + new Class[]{listenerInterface}, >> monitorInvocationHandler); >> + eventListenerProxies.put(listenerInterface, >> listener); >> + } >> + >> + // Add the listener >> + Class<?> listenerListClass = listenerList.getClass(); >> + Method addMethod; >> + try { >> + addMethod = listenerListClass.getMethod("add", >> + new Class<?>[] {Object.class}); >> + } catch (NoSuchMethodException exception) { >> + throw new RuntimeException(exception); >> + } >> + >> + try { >> + addMethod.invoke(listenerList, new Object[] >> {listener}); >> + } catch (IllegalAccessException exception) { >> + throw new RuntimeException(exception); >> + } catch (InvocationTargetException exception) { >> + throw new RuntimeException(exception); >> + } >> + } >> + } >> + } >> + } >> + >> + /** >> + * Un-registers event listeners on a bean. >> + */ >> + private void unregisterEventListeners(Object bean) { >> + Method[] methods = bean.getClass().getMethods(); >> + >> + for (int i = 0; i < methods.length; i++) { >> + Method method = methods[i]; >> + >> + if >> (ListenerList.class.isAssignableFrom(method.getReturnType()) >> + && (method.getModifiers() & Modifier.STATIC) == 0) { >> + ParameterizedType genericType = >> (ParameterizedType)method.getGenericReturnType(); >> + Type[] typeArguments = >> genericType.getActualTypeArguments(); >> + >> + if (typeArguments.length == 1) { >> + Class<?> listenerInterface = >> (Class<?>)typeArguments[0]; >> + >> + // Get the listener list >> + Object listenerList; >> + try { >> + listenerList = method.invoke(bean); >> + } catch (InvocationTargetException exception) { >> + throw new RuntimeException(exception); >> + } catch (IllegalAccessException exception) { >> + throw new RuntimeException(exception); >> + } >> + >> + // Get the listener for this interface >> + Object listener = >> eventListenerProxies.get(listenerInterface); >> + if (listener == null) { >> + throw new IllegalStateException("Listener proxy >> is null."); >> + } >> + >> + // Remove the listener >> + Class<?> listenerListClass = listenerList.getClass(); >> + Method removeMethod; >> + try { >> + removeMethod = >> listenerListClass.getMethod("remove", >> + new Class<?>[] {Object.class}); >> + } catch (NoSuchMethodException exception) { >> + throw new RuntimeException(exception); >> + } >> + >> + try { >> + removeMethod.invoke(listenerList, new Object[] >> {listener}); >> + } catch (IllegalAccessException exception) { >> + throw new RuntimeException(exception); >> + } catch (InvocationTargetException exception) { >> + throw new RuntimeException(exception); >> + } >> + } >> + } >> + } >> + } >> + >> + public ListenerList<BeanMonitorListener> getBeanMonitorListeners() { >> + return beanMonitorListeners; >> + } >> +} >> >> Added: >> incubator/pivot/trunk/core/src/org/apache/pivot/beans/BeanMonitorListener.java >> URL: >> http://svn.apache.org/viewvc/incubator/pivot/trunk/core/src/org/apache/pivot/beans/BeanMonitorListener.java?rev=829797&view=auto >> >> ============================================================================== >> --- >> incubator/pivot/trunk/core/src/org/apache/pivot/beans/BeanMonitorListener.java >> (added) >> +++ >> incubator/pivot/trunk/core/src/org/apache/pivot/beans/BeanMonitorListener.java >> Mon Oct 26 13:48:09 2009 >> @@ -0,0 +1,66 @@ >> +/* >> + * Licensed to the Apache Software Foundation (ASF) under one or more >> + * contributor license agreements. See the NOTICE file distributed with >> + * this work for additional information regarding copyright ownership. >> + * The ASF licenses this file to you 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.apache.pivot.beans; >> + >> +import java.lang.reflect.Method; >> + >> +/** >> + * Bean monitor listener interface. >> + */ >> +public interface BeanMonitorListener { >> + /** >> + * Bean monitor listener adapter. >> + */ >> + public static class Adapter implements BeanMonitorListener { >> + @Override >> + public void sourceChanged(BeanMonitor beanMonitor, Object >> previousSource) { >> + } >> + >> + @Override >> + public void eventFired(BeanMonitor beanMonitor, Method event, >> Object[] arguments) { >> + } >> + >> + @Override >> + public void propertyChanged(BeanMonitor beanMonitor, String >> propertyName) { >> + } >> + } >> + >> + /** >> + * Called when a bean monitor's source has changed. >> + * >> + * @param beanMonitor >> + * @param previousSource >> + */ >> + public void sourceChanged(BeanMonitor beanMonitor, Object >> previousSource); >> + >> + /** >> + * Called when an event has been fired by the bean monitor's source. >> + * >> + * @param beanMonitor >> + * @param event >> + * @param arguments >> + */ >> + public void eventFired(BeanMonitor beanMonitor, Method event, >> Object[] arguments); >> + >> + /** >> + * Called when a property of the bean monitor's source has changed. >> + * >> + * @param beanMonitor >> + * @param propertyName >> + */ >> + public void propertyChanged(BeanMonitor beanMonitor, String >> propertyName); >> +} >> >> Modified: >> incubator/pivot/trunk/tools/src/org/apache/pivot/tools/wtk/ComponentInspectorSkin.java >> URL: >> http://svn.apache.org/viewvc/incubator/pivot/trunk/tools/src/org/apache/pivot/tools/wtk/ComponentInspectorSkin.java?rev=829797&r1=829796&r2=829797&view=diff >> >> ============================================================================== >> --- >> incubator/pivot/trunk/tools/src/org/apache/pivot/tools/wtk/ComponentInspectorSkin.java >> (original) >> +++ >> incubator/pivot/trunk/tools/src/org/apache/pivot/tools/wtk/ComponentInspectorSkin.java >> Mon Oct 26 13:48:09 2009 >> @@ -21,11 +21,12 @@ >> import java.util.Comparator; >> >> import org.apache.pivot.beans.BeanDictionary; >> +import org.apache.pivot.beans.BeanMonitor; >> +import org.apache.pivot.beans.BeanMonitorListener; >> import org.apache.pivot.collections.ArrayList; >> import org.apache.pivot.collections.EnumList; >> import org.apache.pivot.collections.HashMap; >> import org.apache.pivot.collections.List; >> -import org.apache.pivot.collections.Map; >> import org.apache.pivot.serialization.SerializationException; >> import org.apache.pivot.util.Resources; >> import org.apache.pivot.wtk.BoxPane; >> @@ -33,6 +34,7 @@ >> import org.apache.pivot.wtk.ButtonStateListener; >> import org.apache.pivot.wtk.Checkbox; >> import org.apache.pivot.wtk.Component; >> +import org.apache.pivot.wtk.ComponentStateListener; >> import org.apache.pivot.wtk.Dimensions; >> import org.apache.pivot.wtk.FlowPane; >> import org.apache.pivot.wtk.Form; >> @@ -42,7 +44,6 @@ >> import org.apache.pivot.wtk.ListButtonSelectionListener; >> import org.apache.pivot.wtk.Orientation; >> import org.apache.pivot.wtk.Point; >> -import org.apache.pivot.wtk.Rollup; >> import org.apache.pivot.wtk.TextInput; >> import org.apache.pivot.wtk.skin.ContainerSkin; >> import org.apache.pivot.wtk.text.validation.IntValidator; >> @@ -76,13 +77,41 @@ >> >> private Component content = null; >> >> - @WTKX private BoxPane propertiesPane = null; >> + @WTKX private Form propertiesForm = null; >> @WTKX private BoxPane stylesPane = null; >> >> + private BeanDictionary beanDictionary = null; >> + private BeanMonitor beanMonitor = new BeanMonitor(); >> + >> + private HashMap<String, Component> inspectorComponents = new >> HashMap<String, Component>(); >> + >> private static PropertyNameComparator propertyNameComparator = new >> PropertyNameComparator(); >> private static PropertySourceTypeComparator >> propertySourceTypeComparator = >> new PropertySourceTypeComparator(); >> >> + public ComponentInspectorSkin() { >> + beanMonitor.getBeanMonitorListeners().add(new >> BeanMonitorListener.Adapter() { >> + @Override >> + public void propertyChanged(BeanMonitor beanMonitor, String >> propertyName) { >> + Class<?> propertyType = >> beanDictionary.getType(propertyName); >> + >> + if (propertyType == Boolean.TYPE) { >> + updateBooleanControl(propertyName); >> + } else if (propertyType == Integer.TYPE) { >> + updateIntControl(propertyName); >> + } else if (propertyType.isEnum()) { >> + updateEnumControl(propertyName); >> + } else if (propertyType == Point.class) { >> + updatePointControl(propertyName); >> + } else if (propertyType == Dimensions.class) { >> + updateDimensionsControl(propertyName); >> + } else if (propertyType == Limits.class) { >> + updateLimitsControl(propertyName); >> + } >> + } >> + }); >> + } >> + >> @Override >> public void install(Component component) { >> super.install(component); >> @@ -139,25 +168,26 @@ >> >> @Override >> public void sourceChanged(ComponentInspector componentInspector, >> Component previousSource) { >> - propertiesPane.remove(0, propertiesPane.getLength()); >> + Form.SectionSequence propertiesSections = >> propertiesForm.getSections(); >> + propertiesSections.remove(0, propertiesSections.getLength()); >> + >> stylesPane.remove(0, stylesPane.getLength()); >> >> Component source = componentInspector.getSource(); >> >> + beanDictionary = null; >> + beanMonitor.setSource(source); >> + >> if (source != null) { >> - Class<?> sourceType = source.getClass(); >> - BeanDictionary beanDictionary = new BeanDictionary(source); >> + beanDictionary = new BeanDictionary(source); >> >> - Map<Class<?>, List<String>> propertyBuckets = >> + Class<?> sourceType = source.getClass(); >> + HashMap<Class<?>, List<String>> propertyBuckets = >> new HashMap<Class<?>, >> List<String>>(propertySourceTypeComparator); >> >> for (String propertyName : beanDictionary) { >> - boolean readOnly = >> beanDictionary.isReadOnly(propertyName); >> - >> - if (!readOnly) { >> - // TODO? >> - // Class<?> propertyType = >> beanDictionary.getType(propertyName); >> - >> + if (beanMonitor.isNotifyingProperty(propertyName) >> + && !beanDictionary.isReadOnly(propertyName)) { >> Method method = >> BeanDictionary.getGetterMethod(sourceType, propertyName); >> Class<?> declaringClass = method.getDeclaringClass(); >> >> @@ -172,47 +202,42 @@ >> } >> >> for (Class<?> declaringClass : propertyBuckets) { >> - Rollup rollup = new Rollup(); >> - propertiesPane.add(rollup); >> - Label label = new Label(declaringClass.getSimpleName()); >> - label.getStyles().put("color", 16); >> - label.getStyles().put("font", "{bold:true}"); >> - rollup.setHeading(label); >> - >> - Form form = new Form(); >> - form.getStyles().put("rightAlignLabels", true); >> Form.Section section = new Form.Section(); >> - form.getSections().add(section); >> - rollup.setContent(form); >> + section.setHeading(declaringClass.getSimpleName()); >> + propertiesSections.add(section); >> >> for (String propertyName : >> propertyBuckets.get(declaringClass)) { >> - addPropertyControl(propertyName, section, >> beanDictionary); >> + addPropertyControl(propertyName, section); >> } >> } >> } >> } >> >> - private void addPropertyControl(String propertyName, Form.Section >> section, >> - BeanDictionary beanDictionary) { >> + private void addPropertyControl(String propertyName, Form.Section >> section) { >> Class<?> propertyType = beanDictionary.getType(propertyName); >> >> + Component inspectorComponent = null; >> + >> if (propertyType == Boolean.TYPE) { >> - addBooleanControl(propertyName, section, beanDictionary); >> + inspectorComponent = addBooleanControl(propertyName, >> section); >> } else if (propertyType == Integer.TYPE) { >> - addIntControl(propertyName, section, beanDictionary); >> + inspectorComponent = addIntControl(propertyName, section); >> } else if (propertyType.isEnum()) { >> - addEnumControl(propertyName, section, beanDictionary); >> + inspectorComponent = addEnumControl(propertyName, section); >> } else if (propertyType == Point.class) { >> - addPointControl(propertyName, section, beanDictionary); >> + inspectorComponent = addPointControl(propertyName, section); >> } else if (propertyType == Dimensions.class) { >> - addDimensionsControl(propertyName, section, beanDictionary); >> + inspectorComponent = addDimensionsControl(propertyName, >> section); >> } else if (propertyType == Limits.class) { >> - addLimitsControl(propertyName, section, beanDictionary); >> + inspectorComponent = addLimitsControl(propertyName, section); >> + } >> + >> + if (inspectorComponent != null) { >> + inspectorComponents.put(propertyName, inspectorComponent); >> } >> } >> >> - private void addBooleanControl(final String propertyName, >> Form.Section section, >> - final BeanDictionary beanDictionary) { >> + private Component addBooleanControl(final String propertyName, >> Form.Section section) { >> boolean propertyValue = (Boolean)beanDictionary.get(propertyName); >> >> Checkbox checkbox = new Checkbox(); >> @@ -226,11 +251,20 @@ >> beanDictionary.put(propertyName, button.isSelected()); >> } >> }); >> + >> + return checkbox; >> } >> >> - private void addIntControl(final String propertyName, Form.Section >> section, >> - final BeanDictionary beanDictionary) { >> - /* >> + private void updateBooleanControl(String propertyName) { >> + Checkbox checkbox = >> (Checkbox)inspectorComponents.get(propertyName); >> + >> + if (checkbox != null) { >> + boolean propertyValue = >> (Boolean)beanDictionary.get(propertyName); >> + checkbox.setSelected(propertyValue); >> + } >> + } >> + >> + private Component addIntControl(final String propertyName, >> Form.Section section) { >> int propertyValue = (Integer)beanDictionary.get(propertyName); >> >> TextInput textInput = new TextInput(); >> @@ -244,21 +278,33 @@ >> textInput.getComponentStateListeners().add(new >> ComponentStateListener.Adapter() { >> @Override >> public void focusedChanged(Component component, Component >> obverseComponent) { >> - TextInput textInput = (TextInput)component; >> - try { >> - beanDictionary.put(propertyName, >> Integer.parseInt(textInput.getText())); >> - } catch (Exception exception) { >> - Object propertyValue = >> beanDictionary.get(propertyName); >> - textInput.setText(String.valueOf(propertyValue)); >> + if (!component.isFocused()) { >> + TextInput textInput = (TextInput)component; >> + >> + try { >> + beanDictionary.put(propertyName, >> Integer.parseInt(textInput.getText())); >> + } catch (Exception exception) { >> + Object propertyValue = >> beanDictionary.get(propertyName); >> + textInput.setText(String.valueOf(propertyValue)); >> + } >> } >> } >> }); >> - */ >> + >> + return textInput; >> + } >> + >> + private void updateIntControl(String propertyName) { >> + TextInput textInput = >> (TextInput)inspectorComponents.get(propertyName); >> + >> + if (textInput != null) { >> + int propertyValue = >> (Integer)beanDictionary.get(propertyName); >> + textInput.setText(String.valueOf(propertyValue)); >> + } >> } >> >> @SuppressWarnings("unchecked") >> - private void addEnumControl(final String propertyName, Form.Section >> section, >> - final BeanDictionary beanDictionary) { >> + private Component addEnumControl(final String propertyName, >> Form.Section section) { >> Class<?> propertyType = beanDictionary.getType(propertyName); >> Enum<?> propertyValue = (Enum<?>)beanDictionary.get(propertyName); >> >> @@ -274,10 +320,20 @@ >> beanDictionary.put(propertyName, >> listButton.getSelectedItem()); >> } >> }); >> + >> + return listButton; >> } >> >> - private void addPointControl(final String propertyName, Form.Section >> section, >> - final BeanDictionary beanDictionary) { >> + private void updateEnumControl(String propertyName) { >> + ListButton listButton = >> (ListButton)inspectorComponents.get(propertyName); >> + >> + if (listButton != null) { >> + Enum<?> propertyValue = >> (Enum<?>)beanDictionary.get(propertyName); >> + listButton.setSelectedItem(propertyValue); >> + } >> + } >> + >> + private Component addPointControl(final String propertyName, >> Form.Section section) { >> Point point = (Point)beanDictionary.get(propertyName); >> >> BoxPane boxPane = new BoxPane(Orientation.VERTICAL); >> @@ -296,6 +352,23 @@ >> textInput.setText(String.valueOf(point.x)); >> flowPane.add(textInput); >> >> + textInput.getComponentStateListeners().add(new >> ComponentStateListener.Adapter() { >> + @Override >> + public void focusedChanged(Component component, Component >> obverseComponent) { >> + if (!component.isFocused()) { >> + TextInput textInput = (TextInput)component; >> + Point point = >> (Point)beanDictionary.get(propertyName); >> + >> + try { >> + int x = Integer.parseInt(textInput.getText()); >> + beanDictionary.put(propertyName, new Point(x, >> point.y)); >> + } catch (Exception exception) { >> + textInput.setText(String.valueOf(point.x)); >> + } >> + } >> + } >> + }); >> + >> Label label = new Label("x"); >> label.getStyles().put("font", "{italic:true}"); >> flowPane.add(label); >> @@ -312,13 +385,45 @@ >> textInput.setText(String.valueOf(point.y)); >> flowPane.add(textInput); >> >> + textInput.getComponentStateListeners().add(new >> ComponentStateListener.Adapter() { >> + @Override >> + public void focusedChanged(Component component, Component >> obverseComponent) { >> + if (!component.isFocused()) { >> + TextInput textInput = (TextInput)component; >> + Point point = >> (Point)beanDictionary.get(propertyName); >> + >> + try { >> + int y = Integer.parseInt(textInput.getText()); >> + beanDictionary.put(propertyName, new >> Point(point.x, y)); >> + } catch (Exception exception) { >> + textInput.setText(String.valueOf(point.y)); >> + } >> + } >> + } >> + }); >> + >> label = new Label("y"); >> label.getStyles().put("font", "{italic:true}"); >> flowPane.add(label); >> + >> + return boxPane; >> } >> >> - private void addDimensionsControl(final String propertyName, >> Form.Section section, >> - final BeanDictionary beanDictionary) { >> + private void updatePointControl(String propertyName) { >> + BoxPane boxPane = (BoxPane)inspectorComponents.get(propertyName); >> + >> + if (boxPane != null) { >> + Point point = (Point)beanDictionary.get(propertyName); >> + >> + TextInput xTextInput = >> (TextInput)((FlowPane)boxPane.get(0)).get(0); >> + TextInput yTextInput = >> (TextInput)((FlowPane)boxPane.get(1)).get(0); >> + >> + xTextInput.setText(String.valueOf(point.x)); >> + yTextInput.setText(String.valueOf(point.y)); >> + } >> + } >> + >> + private Component addDimensionsControl(final String propertyName, >> Form.Section section) { >> Dimensions dimensions = >> (Dimensions)beanDictionary.get(propertyName); >> >> BoxPane boxPane = new BoxPane(Orientation.VERTICAL); >> @@ -337,6 +442,23 @@ >> textInput.setText(String.valueOf(dimensions.width)); >> flowPane.add(textInput); >> >> + textInput.getComponentStateListeners().add(new >> ComponentStateListener.Adapter() { >> + @Override >> + public void focusedChanged(Component component, Component >> obverseComponent) { >> + if (!component.isFocused()) { >> + TextInput textInput = (TextInput)component; >> + Dimensions dimensions = >> (Dimensions)beanDictionary.get(propertyName); >> + >> + try { >> + int width = >> Integer.parseInt(textInput.getText()); >> + beanDictionary.put(propertyName, new >> Dimensions(width, dimensions.height)); >> + } catch (Exception exception) { >> + >> textInput.setText(String.valueOf(dimensions.width)); >> + } >> + } >> + } >> + }); >> + >> Label label = new Label("width"); >> label.getStyles().put("font", "{italic:true}"); >> flowPane.add(label); >> @@ -353,13 +475,45 @@ >> textInput.setText(String.valueOf(dimensions.height)); >> flowPane.add(textInput); >> >> + textInput.getComponentStateListeners().add(new >> ComponentStateListener.Adapter() { >> + @Override >> + public void focusedChanged(Component component, Component >> obverseComponent) { >> + if (!component.isFocused()) { >> + TextInput textInput = (TextInput)component; >> + Dimensions dimensions = >> (Dimensions)beanDictionary.get(propertyName); >> + >> + try { >> + int height = >> Integer.parseInt(textInput.getText()); >> + beanDictionary.put(propertyName, new >> Dimensions(dimensions.width, height)); >> + } catch (Exception exception) { >> + >> textInput.setText(String.valueOf(dimensions.height)); >> + } >> + } >> + } >> + }); >> + >> label = new Label("height"); >> label.getStyles().put("font", "{italic:true}"); >> flowPane.add(label); >> + >> + return boxPane; >> + } >> + >> + private void updateDimensionsControl(String propertyName) { >> + BoxPane boxPane = (BoxPane)inspectorComponents.get(propertyName); >> + >> + if (boxPane != null) { >> + Dimensions dimensions = >> (Dimensions)beanDictionary.get(propertyName); >> + >> + TextInput widthTextInput = >> (TextInput)((FlowPane)boxPane.get(0)).get(0); >> + TextInput heightTextInput = >> (TextInput)((FlowPane)boxPane.get(1)).get(0); >> + >> + widthTextInput.setText(String.valueOf(dimensions.width)); >> + heightTextInput.setText(String.valueOf(dimensions.height)); >> + } >> } >> >> - private void addLimitsControl(final String propertyName, Form.Section >> section, >> - final BeanDictionary beanDictionary) { >> + private Component addLimitsControl(final String propertyName, >> Form.Section section) { >> Limits limits = (Limits)beanDictionary.get(propertyName); >> >> BoxPane boxPane = new BoxPane(Orientation.VERTICAL); >> @@ -378,6 +532,23 @@ >> textInput.setText(String.valueOf(limits.min)); >> flowPane.add(textInput); >> >> + textInput.getComponentStateListeners().add(new >> ComponentStateListener.Adapter() { >> + @Override >> + public void focusedChanged(Component component, Component >> obverseComponent) { >> + if (!component.isFocused()) { >> + TextInput textInput = (TextInput)component; >> + Limits limits = >> (Limits)beanDictionary.get(propertyName); >> + >> + try { >> + int min = Integer.parseInt(textInput.getText()); >> + beanDictionary.put(propertyName, new Limits(min, >> limits.max)); >> + } catch (Exception exception) { >> + textInput.setText(String.valueOf(limits.min)); >> + } >> + } >> + } >> + }); >> + >> Label label = new Label("min"); >> label.getStyles().put("font", "{italic:true}"); >> flowPane.add(label); >> @@ -394,8 +565,41 @@ >> textInput.setText(String.valueOf(limits.max)); >> flowPane.add(textInput); >> >> + textInput.getComponentStateListeners().add(new >> ComponentStateListener.Adapter() { >> + @Override >> + public void focusedChanged(Component component, Component >> obverseComponent) { >> + if (!component.isFocused()) { >> + TextInput textInput = (TextInput)component; >> + Limits limits = >> (Limits)beanDictionary.get(propertyName); >> + >> + try { >> + int max = Integer.parseInt(textInput.getText()); >> + beanDictionary.put(propertyName, new >> Limits(limits.min, max)); >> + } catch (Exception exception) { >> + textInput.setText(String.valueOf(limits.max)); >> + } >> + } >> + } >> + }); >> + >> label = new Label("max"); >> label.getStyles().put("font", "{italic:true}"); >> flowPane.add(label); >> + >> + return boxPane; >> + } >> + >> + private void updateLimitsControl(String propertyName) { >> + BoxPane boxPane = (BoxPane)inspectorComponents.get(propertyName); >> + >> + if (boxPane != null) { >> + Limits limits = (Limits)beanDictionary.get(propertyName); >> + >> + TextInput minTextInput = >> (TextInput)((FlowPane)boxPane.get(0)).get(0); >> + TextInput maxTextInput = >> (TextInput)((FlowPane)boxPane.get(1)).get(0); >> + >> + minTextInput.setText(String.valueOf(limits.min)); >> + maxTextInput.setText(String.valueOf(limits.max)); >> + } >> } >> } >> >> Modified: >> incubator/pivot/trunk/tools/src/org/apache/pivot/tools/wtk/EventLogger.java >> URL: >> http://svn.apache.org/viewvc/incubator/pivot/trunk/tools/src/org/apache/pivot/tools/wtk/EventLogger.java?rev=829797&r1=829796&r2=829797&view=diff >> >> ============================================================================== >> --- >> incubator/pivot/trunk/tools/src/org/apache/pivot/tools/wtk/EventLogger.java >> (original) >> +++ >> incubator/pivot/trunk/tools/src/org/apache/pivot/tools/wtk/EventLogger.java >> Mon Oct 26 13:48:09 2009 >> @@ -16,68 +16,19 @@ >> */ >> package org.apache.pivot.tools.wtk; >> >> -import java.lang.reflect.InvocationHandler; >> -import java.lang.reflect.InvocationTargetException; >> import java.lang.reflect.Method; >> -import java.lang.reflect.Modifier; >> -import java.lang.reflect.ParameterizedType; >> -import java.lang.reflect.Proxy; >> -import java.lang.reflect.Type; >> -import java.util.Comparator; >> >> -import org.apache.pivot.collections.ArrayList; >> +import org.apache.pivot.beans.BeanMonitor; >> +import org.apache.pivot.beans.BeanMonitorListener; >> import org.apache.pivot.collections.Group; >> -import org.apache.pivot.collections.HashMap; >> import org.apache.pivot.collections.HashSet; >> import org.apache.pivot.collections.Sequence; >> -import org.apache.pivot.collections.immutable.ImmutableList; >> import org.apache.pivot.collections.immutable.ImmutableSet; >> import org.apache.pivot.util.ListenerList; >> -import org.apache.pivot.util.ThreadUtilities; >> -import org.apache.pivot.util.Vote; >> import org.apache.pivot.wtk.Component; >> import org.apache.pivot.wtk.Container; >> >> public class EventLogger extends Container { >> - private static class EventComparator implements Comparator<Method> { >> - @Override >> - public int compare(Method event1, Method event2) { >> - int result = 0; >> - >> - Class<?> listenerInterface1 = event1.getDeclaringClass(); >> - Class<?> listenerInterface2 = event2.getDeclaringClass(); >> - >> - if (listenerInterface1 != listenerInterface2) { >> - result = >> listenerInterface1.getName().compareTo(listenerInterface2.getName()); >> - } >> - >> - if (result == 0) { >> - result = event1.getName().compareTo(event2.getName()); >> - } >> - >> - return result; >> - } >> - } >> - >> - private class LoggerInvocationHandler implements InvocationHandler { >> - @Override >> - public Object invoke(Object proxy, Method event, Object[] >> arguments) throws Throwable { >> - if (includeEvents.contains(event)) { >> - eventLoggerListeners.eventFired(EventLogger.this, event, >> arguments); >> - } >> - >> - Object result = null; >> - Class<?> returnType = event.getReturnType(); >> - if (returnType == Vote.class) { >> - result = Vote.APPROVE; >> - } else if (returnType == Boolean.TYPE) { >> - result = false; >> - } >> - >> - return result; >> - } >> - } >> - >> private static class EventLoggerListenerList extends >> ListenerList<EventLoggerListener> >> implements EventLoggerListener { >> @Override >> @@ -109,12 +60,21 @@ >> } >> } >> >> - private Component source = null; >> + private BeanMonitorListener beanMonitorHandler = new >> BeanMonitorListener.Adapter() { >> + @Override >> + public void sourceChanged(BeanMonitor meanMonitor, Object >> previousSource) { >> + eventLoggerListeners.sourceChanged(EventLogger.this, >> (Component)previousSource); >> + } >> >> - private HashMap<Class<?>, Object> eventListenerProxies = new >> HashMap<Class<?>, Object>(); >> - private LoggerInvocationHandler loggerInvocationHandler = new >> LoggerInvocationHandler(); >> + @Override >> + public void eventFired(BeanMonitor meanMonitor, Method event, >> Object[] arguments) { >> + if (includeEvents.contains(event)) { >> + eventLoggerListeners.eventFired(EventLogger.this, event, >> arguments); >> + } >> + } >> + }; >> >> - private ArrayList<Method> declaredEvents = new ArrayList<Method>(new >> EventComparator()); >> + private BeanMonitor beanMonitor = new BeanMonitor(); >> >> private HashSet<Method> includeEvents = new HashSet<Method>(); >> >> @@ -125,34 +85,21 @@ >> } >> >> public EventLogger(Component source) { >> + beanMonitor.getBeanMonitorListeners().add(beanMonitorHandler); >> setSource(source); >> setSkin(new EventLoggerSkin()); >> } >> >> public Component getSource() { >> - return source; >> + return (Component)beanMonitor.getSource(); >> } >> >> public void setSource(Component source) { >> - Component previousSource = this.source; >> - >> - if (source != previousSource) { >> - this.source = source; >> - >> - if (previousSource != null) { >> - unregisterEventListeners(previousSource); >> - } >> - >> - if (source != null) { >> - registerEventListeners(source); >> - } >> - >> - eventLoggerListeners.sourceChanged(this, previousSource); >> - } >> + beanMonitor.setSource(source); >> } >> >> public Sequence<Method> getDeclaredEvents() { >> - return new ImmutableList<Method>(declaredEvents); >> + return beanMonitor.getDeclaredEvents(); >> } >> >> public Group<Method> getIncludeEvents() { >> @@ -177,79 +124,6 @@ >> return includeEvents.contains(event); >> } >> >> - private void registerEventListeners(Component source) { >> - declaredEvents.clear(); >> - >> - Method[] methods = source.getClass().getMethods(); >> - >> - for (int i = 0; i < methods.length; i++) { >> - Method method = methods[i]; >> - >> - if >> (ListenerList.class.isAssignableFrom(method.getReturnType()) >> - && (method.getModifiers() & Modifier.STATIC) == 0) { >> - ParameterizedType genericType = >> (ParameterizedType)method.getGenericReturnType(); >> - Type[] typeArguments = >> genericType.getActualTypeArguments(); >> - >> - if (typeArguments.length == 1) { >> - Class<?> listenerInterface = >> (Class<?>)typeArguments[0]; >> - >> - if (!listenerInterface.isInterface()) { >> - throw new >> RuntimeException(listenerInterface.getName() >> - + " is not an interface."); >> - } >> - >> - Method[] interfaceMethods = >> listenerInterface.getMethods(); >> - for (int j = 0; j < interfaceMethods.length; j++) { >> - Method interfaceMethod = interfaceMethods[j]; >> - declaredEvents.add(interfaceMethod); >> - } >> - >> - // Get the listener list >> - Object listenerList; >> - try { >> - listenerList = method.invoke(source); >> - } catch (InvocationTargetException exception) { >> - throw new RuntimeException(exception); >> - } catch (IllegalAccessException exception) { >> - throw new RuntimeException(exception); >> - } >> - >> - // Get the listener for this interface >> - Object listener = >> eventListenerProxies.get(listenerInterface); >> - if (listener == null) { >> - listener = >> Proxy.newProxyInstance(ThreadUtilities.getClassLoader(), >> - new Class[]{listenerInterface}, >> loggerInvocationHandler); >> - eventListenerProxies.put(listenerInterface, >> listener); >> - } >> - >> - // Add the listener >> - Class<?> listenerListClass = listenerList.getClass(); >> - Method addMethod; >> - try { >> - addMethod = listenerListClass.getMethod("add", >> - new Class<?>[] {Object.class}); >> - } catch (NoSuchMethodException exception) { >> - throw new RuntimeException(exception); >> - } >> - >> - try { >> - addMethod.invoke(listenerList, new Object[] >> {listener}); >> - } catch (IllegalAccessException exception) { >> - throw new RuntimeException(exception); >> - } catch (InvocationTargetException exception) { >> - throw new RuntimeException(exception); >> - } >> - } >> - } >> - } >> - } >> - >> - private void unregisterEventListeners(Component source) { >> - declaredEvents.clear(); >> - >> - // TODO >> - } >> - >> public ListenerList<EventLoggerListener> getEventLoggerListeners() { >> return eventLoggerListeners; >> } >> >> Modified: >> incubator/pivot/trunk/tools/src/org/apache/pivot/tools/wtk/component_inspector_skin.wtkx >> URL: >> http://svn.apache.org/viewvc/incubator/pivot/trunk/tools/src/org/apache/pivot/tools/wtk/component_inspector_skin.wtkx?rev=829797&r1=829796&r2=829797&view=diff >> >> ============================================================================== >> --- >> incubator/pivot/trunk/tools/src/org/apache/pivot/tools/wtk/component_inspector_skin.wtkx >> (original) >> +++ >> incubator/pivot/trunk/tools/src/org/apache/pivot/tools/wtk/component_inspector_skin.wtkx >> Mon Oct 26 13:48:09 2009 >> @@ -22,7 +22,10 @@ >> <view> >> <BoxPane orientation="vertical" styles="{fill:true, >> horizontalAlignment:'right'}"> >> <Label text="%properties" styles="{padding:{left:10, top:5}, >> font:{bold:true}}"/> >> - <BoxPane wtkx:id="propertiesPane" orientation="vertical" >> styles="{padding:{left:10}}"/> >> + <BoxPane orientation="vertical" styles="{padding:{left:10}}"> >> + <Form wtkx:id="propertiesForm" >> + styles="{rightAlignLabels:true, >> showFirstSectionHeading:true}"/> >> + </BoxPane> >> <Separator/> >> <Label text="%styles" styles="{padding:{left:10}, >> font:{bold:true}}"/> >> <BoxPane wtkx:id="stylesPane" orientation="vertical" >> styles="{padding:{left:10}}"/> >> >> >> >
