001: /*
002: * Copyright (c) 2000, Jacob Smullyan.
003: *
004: * This is part of SkunkDAV, a WebDAV client. See http://skunkdav.sourceforge.net/
005: * for the latest version.
006: *
007: * SkunkDAV is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License as published
009: * by the Free Software Foundation; either version 2, or (at your option)
010: * any later version.
011: *
012: * SkunkDAV is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with SkunkDAV; see the file COPYING. If not, write to the Free
019: * Software Foundation, 59 Temple Place - Suite 330, Boston, MA
020: * 02111-1307, USA.
021: */
022:
023: package org.skunk.dav.client.gui;
024:
025: import java.beans.BeanInfo;
026: import java.beans.IntrospectionException;
027: import java.beans.Introspector;
028: import java.beans.PropertyDescriptor;
029: import java.lang.reflect.InvocationTargetException;
030: import java.lang.reflect.Method;
031: import java.util.ArrayList;
032: import java.util.Collections;
033: import java.util.HashMap;
034: import java.util.Iterator;
035: import java.util.List;
036: import java.util.Map;
037: import org.skunk.trace.Debug;
038:
039: /**
040: * a property can be registered with the state monitor so that when the
041: * a given application property changes state, so will the registered property,
042: * according to a mapping of property values. If the mapping is null,
043: * the application property value is used directly to set the target property value.
044: */
045: public class StateMonitor {
046: // private static Map applicationValues;
047: private static List monitors;
048:
049: private Object target;
050: private String targetProperty;
051: private String applicationProperty;
052: private Mapper valueMapper;
053: private DomainMatcher domainMatcher;
054:
055: private Method setter;
056:
057: static {
058: //applicationValues = Collections.synchronizedMap(new HashMap());
059: monitors = Collections.synchronizedList(new ArrayList());
060: }
061:
062: /**
063: * set a target object's property value to vary according
064: * to changes in the value of the application property, which
065: * is maintained statically by the StateMonitor class. The
066: * target property's value is set to whatever the valueMapper
067: * returns, or, if the valueMapper is null, to the value of
068: * the application property.
069: * @param target the Object whose property will be set
070: * @param targetProperty the property of the Object that will be set
071: * @param applicationProperty the application property according to which the target property will vary
072: * @param valueMapper the Mapper that maps application values to target values, or null
073: * @param domainMatcher the DomainMatcher that tests domainKeys, or null
074: */
075: public static void registerProperty(Object target,
076: String targetProperty, String applicationProperty,
077: Mapper valueMapper, DomainMatcher domainMatcher) {
078: unregisterProperty(target, targetProperty, applicationProperty);
079: monitors.add(new StateMonitor(target, targetProperty,
080: applicationProperty, valueMapper, domainMatcher));
081: }
082:
083: public static void unregisterProperty(Object target,
084: String targetProperty, String applicationProperty) {
085: for (Iterator it = monitors.iterator(); it.hasNext();) {
086: StateMonitor sm = (StateMonitor) it.next();
087: if (sm.target.equals(target)
088: && sm.targetProperty.equals(targetProperty)
089: && sm.applicationProperty
090: .equals(applicationProperty)) {
091: monitors.remove(sm);
092: break;
093: }
094: }
095: }
096:
097: public static final void setProperty(String applicationProperty,
098: Object value, Object domainKey) {
099: // applicationValues.put(applicationProperty, value);
100: for (Iterator it = monitors.iterator(); it.hasNext();) {
101: StateMonitor nextMonitor = (StateMonitor) it.next();
102: if (nextMonitor.applicationProperty
103: .equals(applicationProperty)) {
104: DomainMatcher dm = nextMonitor.domainMatcher;
105: if (dm == null || dm.domainMatches(domainKey))
106: nextMonitor.configureTarget(value);
107: }
108: }
109: }
110:
111: public interface DomainMatcher {
112: boolean domainMatches(Object domainKey);
113: }
114:
115: /**
116: * maps values of the application property to values of the target property
117: */
118: public interface Mapper {
119: /**
120: * @param applicationValue the application value of the registered application property
121: * @return target value that the StateMonitor should set registered target properties
122: */
123: Object getTargetValue(Object applicationValue);
124:
125: /**
126: * @return whether or not the Mapper can map this value
127: */
128: boolean canMapValue(Object applicationValue);
129: }
130:
131: /**
132: * @return a Mapper object that simply wraps a Map object
133: */
134: public static Mapper getMapper(final Map m) {
135: return new Mapper() {
136: public Object getTargetValue(Object applicationValue) {
137: return (m.containsKey(applicationValue)) ? m
138: .get(applicationValue) : null;
139: }
140:
141: public boolean canMapValue(Object applicationValue) {
142: return m.containsKey(applicationValue);
143: }
144: };
145: }
146:
147: private StateMonitor(Object target, String targetProperty,
148: String applicationProperty, Mapper valueMapper,
149: DomainMatcher domainMatcher) {
150: this .target = target;
151: this .targetProperty = targetProperty;
152: this .applicationProperty = applicationProperty;
153: this .valueMapper = valueMapper;
154: this .domainMatcher = domainMatcher;
155: }
156:
157: private void configureTarget(Object applicationValue) {
158: if (valueMapper == null
159: || valueMapper.canMapValue(applicationValue)) {
160: try {
161: if (setter == null) //lazy initialization of setter method
162: {
163: BeanInfo info = Introspector.getBeanInfo(target
164: .getClass());
165: PropertyDescriptor[] propDescriptors = info
166: .getPropertyDescriptors();
167: for (int i = 0; i < propDescriptors.length; i++) {
168: PropertyDescriptor pd = propDescriptors[i];
169: String propName = pd.getName();
170: if (propName.equals(targetProperty)) {
171: setter = pd.getWriteMethod();
172: break;
173: }
174: }
175: }
176: if (setter != null) {
177: Object targetValue = (valueMapper != null) ? valueMapper
178: .getTargetValue(applicationValue)
179: : applicationValue;
180: setter.invoke(target, new Object[] { targetValue });
181: } else {
182: Debug.trace(this , Debug.DP2,
183: "*** Error: setter not found for "
184: + targetProperty);
185: }
186:
187: } catch (IntrospectionException intro) {
188: Debug.trace(this , Debug.DP2, intro);
189: } catch (IllegalAccessException illAcc) {
190: Debug.trace(this , Debug.DP2, illAcc);
191: } catch (IllegalArgumentException illArg) {
192: Debug.trace(this , Debug.DP2, illArg);
193: } catch (InvocationTargetException invoTarg) {
194: Debug.trace(this , Debug.DP2, invoTarg);
195: }
196: }
197: }
198: }
199:
200: /* $Log: StateMonitor.java,v $
201: /* Revision 1.7 2000/12/19 22:36:05 smulloni
202: /* adjustments to preamble.
203: /*
204: /* Revision 1.6 2000/12/03 23:53:26 smulloni
205: /* added license and copyright preamble to java files.
206: /*
207: /* Revision 1.5 2000/11/28 00:01:39 smullyan
208: /* added a status bar/minibuffer, with a location field showing the current line and
209: /* column number (for the SimpleTextEditor and kin only).
210: /*
211: /* Revision 1.4 2000/11/23 04:37:32 smullyan
212: /* editor and explorer now synch their files using the StateMonitor, which has
213: /* been improved significantly
214: /*
215: /* Revision 1.3 2000/11/22 00:11:27 smullyan
216: /* editor now locks and unlocks, more or less appropriately.
217: /* */
|