View | Details | Raw Unified | Return to bug 24694 | Differences between
and this patch

Collapse All | Expand All

(-)PropertySheetEntry.java (-675 / +694 lines)
Lines 36-723 Link Here
36
 * and <code>IPropertyDescriptor</code> to interact with domain model objects.
36
 * and <code>IPropertyDescriptor</code> to interact with domain model objects.
37
 * <p>
37
 * <p>
38
 * Every property sheet entry has a single descriptor (except the root entry
38
 * Every property sheet entry has a single descriptor (except the root entry
39
 * which has none). This descriptor determines what property
39
 * which has none). This descriptor determines what property of its objects it
40
 * of its objects it will display/edit.
40
 * will display/edit.
41
 * </p>
41
 * </p>
42
 * <p>
42
 * <p>
43
 * Entries do not listen for changes in their objects. Since there is no
43
 * Entries do not listen for changes in their objects. Since there is no
44
 * restriction on properties being independent, a change in one property
44
 * restriction on properties being independent, a change in one property may
45
 * may affect other properties. The value of a parent's property may also
45
 * affect other properties. The value of a parent's property may also change. As
46
 * change. As a result we are forced to refresh the entire entry tree
46
 * a result we are forced to refresh the entire entry tree when a property
47
 * when a property changes value.
47
 * changes value.
48
 * </p>
48
 * </p>
49
 * 
49
 * 
50
 * @since 3.0 (was previously internal)
50
 * @since 3.0 (was previously internal)
51
 */
51
 */
52
public class PropertySheetEntry implements IPropertySheetEntry {
52
public class PropertySheetEntry implements IPropertySheetEntry {
53
53
54
    /**
54
	/**
55
     * The values we are displaying/editing.
55
	 * The values we are displaying/editing. These objects repesent the value of
56
     * These objects repesent the value of one of the
56
	 * one of the properties of the values of our parent entry. Except for the
57
     * properties of the values of our parent entry.
57
	 * root entry where they represent the input (selected) objects.
58
     * Except for the root entry where they represent the
58
	 */
59
     * input (selected) objects.
59
	private Object[] values = new Object[0];
60
     */
60
61
    private Object[] values = new Object[0];
61
	/**
62
62
	 * The property sources for the values we are displaying/editing.
63
    /**
63
	 */
64
     * The property sources for the values we are displaying/editing.
64
	private Map sources = new HashMap(0);
65
     */
65
66
    private Map sources = new HashMap(0);
66
	/**
67
67
	 * The value of this entry is defined as the the first object in its value
68
    /**
68
	 * array or, if that object is an <code>IPropertySource</code>, the value
69
     * The value of this entry is defined as the the first object
69
	 * it returns when sent <code>getEditableValue</code>
70
     * in its value array or, if that object is an 
70
	 */
71
     * <code>IPropertySource</code>, the value it returns when sent
71
	private Object editValue;
72
     * <code>getEditableValue</code>
72
73
     */
73
	private PropertySheetEntry parent;
74
    private Object editValue;
74
75
75
	private IPropertySourceProvider propertySourceProvider;
76
    private PropertySheetEntry parent;
76
77
77
	private IPropertyDescriptor descriptor;
78
    private IPropertySourceProvider propertySourceProvider;
78
79
79
	private CellEditor editor;
80
    private IPropertyDescriptor descriptor;
80
81
81
	private String errorText;
82
    private CellEditor editor;
82
83
83
	private PropertySheetEntry[] childEntries = null;
84
    private String errorText;
84
85
85
	private ListenerList listeners = new ListenerList();
86
    private PropertySheetEntry[] childEntries = null;
86
87
87
	/**
88
    private ListenerList listeners = new ListenerList();
88
	 * Create the CellEditorListener for this entry. It listens for value
89
89
	 * changes in the CellEditor, and cancel and finish requests.
90
    /**
90
	 */
91
     * Create the CellEditorListener for this entry. It listens for
91
	private ICellEditorListener cellEditorListener = new ICellEditorListener() {
92
     * value changes in the CellEditor, and cancel and finish requests.
92
		public void editorValueChanged(boolean oldValidState,
93
     */
93
				boolean newValidState) {
94
    private ICellEditorListener cellEditorListener = new ICellEditorListener() {
94
			if (!newValidState)
95
        public void editorValueChanged(boolean oldValidState,
95
				// currently not valid so show an error message
96
                boolean newValidState) {
96
				setErrorText(editor.getErrorMessage());
97
            if (!newValidState)
97
			else
98
                // currently not valid so show an error message
98
				// currently valid
99
                setErrorText(editor.getErrorMessage());
99
				setErrorText(null);
100
            else
100
		}
101
                // currently valid 
101
102
                setErrorText(null);
102
		public void cancelEditor() {
103
        }
103
			setErrorText(null);
104
104
		}
105
        public void cancelEditor() {
105
106
            setErrorText(null);
106
		public void applyEditorValue() {
107
        }
107
			PropertySheetEntry.this.applyEditorValue();
108
108
		}
109
        public void applyEditorValue() {
109
	};
110
            PropertySheetEntry.this.applyEditorValue();
110
111
        }
111
	/*
112
    };
112
	 * (non-Javadoc) Method declared on IPropertySheetEntry.
113
113
	 */
114
    /* (non-Javadoc)
114
	public void addPropertySheetEntryListener(
115
     * Method declared on IPropertySheetEntry.
115
			IPropertySheetEntryListener listener) {
116
     */
116
		listeners.add(listener);
117
    public void addPropertySheetEntryListener(
117
	}
118
            IPropertySheetEntryListener listener) {
118
119
        listeners.add(listener);
119
	/*
120
    }
120
	 * (non-Javadoc) Method declared on IPropertySheetEntry.
121
121
	 */
122
    /* (non-Javadoc)
122
	public void applyEditorValue() {
123
     * Method declared on IPropertySheetEntry.
123
		if (editor == null)
124
     */
124
			return;
125
    public void applyEditorValue() {
125
126
        if (editor == null)
126
		// Check if editor has a valid value
127
            return;
127
		if (!editor.isValueValid()) {
128
128
			setErrorText(editor.getErrorMessage());
129
        // Check if editor has a valid value
129
			return;
130
        if (!editor.isValueValid()) {
130
		}
131
            setErrorText(editor.getErrorMessage());
131
132
            return;
132
		setErrorText(null);
133
        }
133
134
134
		// See if the value changed and if so update
135
        setErrorText(null);
135
		Object newValue = editor.getValue();
136
136
		boolean changed = false;
137
        // See if the value changed and if so update
137
		if (values.length > 1) {
138
        Object newValue = editor.getValue();
138
			changed = true;
139
        boolean changed = false;
139
		} else if (editValue == null) {
140
        if (values.length > 1) {
140
			if (newValue != null)
141
            changed = true;
141
				changed = true;
142
        } else if (editValue == null) {
142
		} else if (!editValue.equals(newValue))
143
            if (newValue != null)
143
			changed = true;
144
                changed = true;
144
145
        } else if (!editValue.equals(newValue))
145
		// Set the editor value
146
            changed = true;
146
		if (changed)
147
147
			setValue(newValue);
148
        // Set the editor value
148
	}
149
        if (changed)
149
150
            setValue(newValue);
150
	/**
151
    }
151
	 * Return the sorted intersection of all the
152
152
	 * <code>IPropertyDescriptor</code>s for the objects.
153
    /**
153
	 * 
154
     * Return the sorted intersection of all the <code>IPropertyDescriptor</code>s 
154
	 * @return List
155
     * for the objects.
155
	 */
156
     * @return List
156
	private List computeMergedPropertyDescriptors() {
157
     */
157
		if (values.length == 0)
158
    private List computeMergedPropertyDescriptors() {
158
			return new ArrayList(0);
159
        if (values.length == 0)
159
160
            return new ArrayList(0);
160
		// get all descriptors from each object
161
161
		Map[] propertyDescriptorMaps = new Map[values.length];
162
        // get all descriptors from each object
162
		for (int i = 0; i < values.length; i++) {
163
        Map[] propertyDescriptorMaps = new Map[values.length];
163
			Object object = values[i];
164
        for (int i = 0; i < values.length; i++) {
164
			IPropertySource source = getPropertySource(object);
165
            Object object = values[i];
165
			if (source == null) {
166
            IPropertySource source = getPropertySource(object);
166
				// if one of the selected items is not a property source
167
            if (source == null) {
167
				// then we show no properties
168
                // if one of the selected items is not a property source
168
				return new ArrayList(0);
169
                // then we show no properties
169
			}
170
                return new ArrayList(0);
170
			// get the property descriptors keyed by id
171
            }
171
			propertyDescriptorMaps[i] = computePropertyDescriptorsFor(source);
172
            // get the property descriptors keyed by id
172
		}
173
            propertyDescriptorMaps[i] = computePropertyDescriptorsFor(source);
173
174
        }
174
		// intersect
175
175
		Map intersection = propertyDescriptorMaps[0];
176
        // intersect
176
		for (int i = 1; i < propertyDescriptorMaps.length; i++) {
177
        Map intersection = propertyDescriptorMaps[0];
177
			// get the current ids
178
        for (int i = 1; i < propertyDescriptorMaps.length; i++) {
178
			Object[] ids = intersection.keySet().toArray();
179
            // get the current ids
179
			for (int j = 0; j < ids.length; j++) {
180
            Object[] ids = intersection.keySet().toArray();
180
				Object object = propertyDescriptorMaps[i].get(ids[j]);
181
            for (int j = 0; j < ids.length; j++) {
181
				if (object == null
182
                Object object = propertyDescriptorMaps[i].get(ids[j]);
182
						||
183
                if (object == null
183
						// see if the descriptors (which have the same id) are
184
                        ||
184
						// compatible
185
                        // see if the descriptors (which have the same id) are compatible
185
						!((IPropertyDescriptor) intersection.get(ids[j]))
186
                        !((IPropertyDescriptor) intersection.get(ids[j]))
186
								.isCompatibleWith((IPropertyDescriptor) object))
187
                                .isCompatibleWith((IPropertyDescriptor) object))
187
					intersection.remove(ids[j]);
188
                    intersection.remove(ids[j]);
188
			}
189
            }
189
		}
190
        }
190
191
191
		// Sort the descriptors
192
        // Sort the descriptors	
192
		List descriptors = new ArrayList(intersection.values());
193
        List descriptors = new ArrayList(intersection.values());
193
		Collections.sort(descriptors, new Comparator() {
194
        Collections.sort(descriptors, new Comparator() {
194
			Collator coll = Collator.getInstance(Locale.getDefault());
195
            Collator coll = Collator.getInstance(Locale.getDefault());
195
196
196
			public int compare(Object a, Object b) {
197
            public int compare(Object a, Object b) {
197
				IPropertyDescriptor d1, d2;
198
                IPropertyDescriptor d1, d2;
198
				String dname1, dname2;
199
                String dname1, dname2;
199
				d1 = (IPropertyDescriptor) a;
200
                d1 = (IPropertyDescriptor) a;
200
				dname1 = d1.getDisplayName();
201
                dname1 = d1.getDisplayName();
201
				d2 = (IPropertyDescriptor) b;
202
                d2 = (IPropertyDescriptor) b;
202
				dname2 = d2.getDisplayName();
203
                dname2 = d2.getDisplayName();
203
				return coll.compare(dname1, dname2);
204
                return coll.compare(dname1, dname2);
204
			}
205
            }
205
		});
206
        });
206
207
207
		return descriptors;
208
        return descriptors;
208
	}
209
    }
209
210
210
	/**
211
    /**
211
	 * Returns an map of property descritptors (keyed on id) for the given
212
     * Returns an map of property descritptors (keyed on id) for the 
212
	 * property source.
213
     * given property source.
213
	 * 
214
     *
214
	 * @param source
215
     * @param source a property source for which to obtain descriptors
215
	 *            a property source for which to obtain descriptors
216
     * @return a table of decriptors keyed on their id
216
	 * @return a table of decriptors keyed on their id
217
     */
217
	 */
218
    private Map computePropertyDescriptorsFor(IPropertySource source) {
218
	private Map computePropertyDescriptorsFor(IPropertySource source) {
219
        IPropertyDescriptor[] descriptors = source.getPropertyDescriptors();
219
		IPropertyDescriptor[] descriptors = source.getPropertyDescriptors();
220
        Map result = new HashMap(descriptors.length * 2 + 1);
220
		Map result = new HashMap(descriptors.length * 2 + 1);
221
        for (int i = 0; i < descriptors.length; i++) {
221
		for (int i = 0; i < descriptors.length; i++) {
222
            result.put(descriptors[i].getId(), descriptors[i]);
222
			result.put(descriptors[i].getId(), descriptors[i]);
223
        }
223
		}
224
        return result;
224
		return result;
225
    }
225
	}
226
226
227
    /**
227
	/**
228
     * Create our child entries.
228
	 * Create our child entries.
229
     */
229
	 */
230
    private void createChildEntries() {
230
	private void createChildEntries() {
231
        // get the current descriptors
231
		// get the current descriptors
232
        List descriptors = computeMergedPropertyDescriptors();
232
		List descriptors = computeMergedPropertyDescriptors();
233
233
234
        // rebuild child entries using old when possible
234
		// rebuild child entries using old when possible
235
        childEntries = new PropertySheetEntry[descriptors.size()];
235
		childEntries = new PropertySheetEntry[descriptors.size()];
236
        for (int i = 0; i < descriptors.size(); i++) {
236
		for (int i = 0; i < descriptors.size(); i++) {
237
            IPropertyDescriptor d = (IPropertyDescriptor) descriptors.get(i);
237
			IPropertyDescriptor d = (IPropertyDescriptor) descriptors.get(i);
238
            // create new entry
238
			// create new entry
239
            PropertySheetEntry entry = new PropertySheetEntry();
239
			PropertySheetEntry entry = createChildEntry();
240
            entry.setDescriptor(d);
240
			entry.setDescriptor(d);
241
            entry.setParent(this);
241
			entry.setParent(this);
242
            entry.setPropertySourceProvider(propertySourceProvider);
242
			entry.setPropertySourceProvider(propertySourceProvider);
243
            entry.refreshValues();
243
			entry.refreshValues();
244
            childEntries[i] = entry;
244
			childEntries[i] = entry;
245
        }
245
		}
246
    }
246
	}
247
247
248
    /* (non-Javadoc)
248
	/**
249
     * Method declared on IPropertySheetEntry.
249
	 * Factory method to create a new child <code>PropertySheetEntry</code>
250
     */
250
	 * instance.
251
    public void dispose() {
251
	 * <p>
252
        if (editor != null) {
252
	 * Subclasses may overwrite to create new instances of their own class.
253
            editor.dispose();
253
	 * </p>
254
            editor = null;
254
	 * 
255
        }
255
	 * @return a new <code>PropertySheetEntry</code> instance for the
256
        // recursive call to dispose children
256
	 *         descriptor passed in
257
        if (childEntries != null)
257
	 */
258
            for (int i = 0; i < childEntries.length; i++) {
258
	protected PropertySheetEntry createChildEntry() {
259
                // an error in a property source may cause refreshChildEntries
259
		return new PropertySheetEntry();
260
                // to fail. Since the Workbench handles such errors we
260
	}
261
                // can be left in a state where a child entry is null.
261
262
                if (childEntries[i] != null)
262
	/*
263
                    childEntries[i].dispose();
263
	 * (non-Javadoc) Method declared on IPropertySheetEntry.
264
            }
264
	 */
265
    }
265
	public void dispose() {
266
266
		if (editor != null) {
267
    /**
267
			editor.dispose();
268
     * The child entries of this entry have changed 
268
			editor = null;
269
     * (children added or removed).
269
		}
270
     * Notify all listeners of the change.
270
		// recursive call to dispose children
271
     */
271
		if (childEntries != null)
272
    private void fireChildEntriesChanged() {
272
			for (int i = 0; i < childEntries.length; i++) {
273
        if (listeners == null)
273
				// an error in a property source may cause refreshChildEntries
274
            return;
274
				// to fail. Since the Workbench handles such errors we
275
        Object[] array = listeners.getListeners();
275
				// can be left in a state where a child entry is null.
276
        for (int i = 0; i < array.length; i++) {
276
				if (childEntries[i] != null)
277
            IPropertySheetEntryListener listener = (IPropertySheetEntryListener) array[i];
277
					childEntries[i].dispose();
278
            listener.childEntriesChanged(this);
278
			}
279
        }
279
	}
280
    }
280
281
281
	/**
282
    /**
282
	 * The child entries of this entry have changed (children added or removed).
283
     * The error message of this entry has changed.
283
	 * Notify all listeners of the change.
284
     * Notify all listeners of the change.
284
	 */
285
     */
285
	private void fireChildEntriesChanged() {
286
    private void fireErrorMessageChanged() {
286
		if (listeners == null)
287
        if (listeners == null)
287
			return;
288
            return;
288
		Object[] array = listeners.getListeners();
289
        Object[] array = listeners.getListeners();
289
		for (int i = 0; i < array.length; i++) {
290
        for (int i = 0; i < array.length; i++) {
290
			IPropertySheetEntryListener listener = (IPropertySheetEntryListener) array[i];
291
            IPropertySheetEntryListener listener = (IPropertySheetEntryListener) array[i];
291
			listener.childEntriesChanged(this);
292
            listener.errorMessageChanged(this);
292
		}
293
        }
293
	}
294
    }
294
295
295
	/**
296
    /**
296
	 * The error message of this entry has changed. Notify all listeners of the
297
     * The values of this entry have changed.
297
	 * change.
298
     * Notify all listeners of the change.
298
	 */
299
     */
299
	private void fireErrorMessageChanged() {
300
    private void fireValueChanged() {
300
		if (listeners == null)
301
        if (listeners == null)
301
			return;
302
            return;
302
		Object[] array = listeners.getListeners();
303
        Object[] array = listeners.getListeners();
303
		for (int i = 0; i < array.length; i++) {
304
        for (int i = 0; i < array.length; i++) {
304
			IPropertySheetEntryListener listener = (IPropertySheetEntryListener) array[i];
305
            IPropertySheetEntryListener listener = (IPropertySheetEntryListener) array[i];
305
			listener.errorMessageChanged(this);
306
            listener.valueChanged(this);
306
		}
307
        }
307
	}
308
    }
308
309
309
	/**
310
    /* (non-Javadoc)
310
	 * The values of this entry have changed. Notify all listeners of the
311
     * Method declared on IPropertySheetEntry.
311
	 * change.
312
     */
312
	 */
313
    public String getCategory() {
313
	private void fireValueChanged() {
314
        return descriptor.getCategory();
314
		if (listeners == null)
315
    }
315
			return;
316
316
		Object[] array = listeners.getListeners();
317
    /* (non-Javadoc)
317
		for (int i = 0; i < array.length; i++) {
318
     * Method declared on IPropertySheetEntry.
318
			IPropertySheetEntryListener listener = (IPropertySheetEntryListener) array[i];
319
     */
319
			listener.valueChanged(this);
320
    public IPropertySheetEntry[] getChildEntries() {
320
		}
321
        if (childEntries == null)
321
	}
322
            createChildEntries();
322
323
        return childEntries;
323
	/*
324
    }
324
	 * (non-Javadoc) Method declared on IPropertySheetEntry.
325
325
	 */
326
    /* (non-Javadoc)
326
	public String getCategory() {
327
     * Method declared on IPropertySheetEntry.
327
		return descriptor.getCategory();
328
     */
328
	}
329
    public String getDescription() {
329
330
        return descriptor.getDescription();
330
	/*
331
    }
331
	 * (non-Javadoc) Method declared on IPropertySheetEntry.
332
332
	 */
333
    /**
333
	public IPropertySheetEntry[] getChildEntries() {
334
     *  Returns the descriptor for this entry.
334
		if (childEntries == null)
335
     * @return IPropertyDescriptor
335
			createChildEntries();
336
     */
336
		return childEntries;
337
    private IPropertyDescriptor getDescriptor() {
337
	}
338
        return descriptor;
338
339
    }
339
	/*
340
340
	 * (non-Javadoc) Method declared on IPropertySheetEntry.
341
    /* (non-Javadoc)
341
	 */
342
     * Method declared on IPropertySheetEntry.
342
	public String getDescription() {
343
     */
343
		return descriptor.getDescription();
344
    public String getDisplayName() {
344
	}
345
        return descriptor.getDisplayName();
345
346
    }
346
	/**
347
347
	 * Returns the descriptor for this entry.
348
    /*
348
	 * 
349
     *  (non-Javadoc)
349
	 * @return IPropertyDescriptor
350
     * @see org.eclipse.ui.views.properties.IPropertySheetEntry#getEditor(org.eclipse.swt.widgets.Composite)
350
	 */
351
     */
351
	private IPropertyDescriptor getDescriptor() {
352
    public CellEditor getEditor(Composite parent) {
352
		return descriptor;
353
353
	}
354
        if (editor == null) {
354
355
            editor = descriptor.createPropertyEditor(parent);
355
	/*
356
            if (editor != null) {
356
	 * (non-Javadoc) Method declared on IPropertySheetEntry.
357
                editor.addListener(cellEditorListener);
357
	 */
358
            }
358
	public String getDisplayName() {
359
        }
359
		return descriptor.getDisplayName();
360
        if (editor != null) {
360
	}
361
            editor.setValue(editValue);
361
362
            setErrorText(editor.getErrorMessage());
362
	/*
363
        }
363
	 * (non-Javadoc)
364
        return editor;
364
	 * 
365
    }
365
	 * @see org.eclipse.ui.views.properties.IPropertySheetEntry#getEditor(org.eclipse.swt.widgets.Composite)
366
366
	 */
367
    /**
367
	public CellEditor getEditor(Composite parent) {
368
     * Returns the edit value for the object at the given index.
368
369
     *
369
		if (editor == null) {
370
     * @param index the value object index
370
			editor = descriptor.createPropertyEditor(parent);
371
     * @return the edit value for the object at the given index
371
			if (editor != null) {
372
     */
372
				editor.addListener(cellEditorListener);
373
    protected Object getEditValue(int index) {
373
			}
374
        Object value = values[index];
374
		}
375
        IPropertySource source = getPropertySource(value);
375
		if (editor != null) {
376
        if (source != null) {
376
			editor.setValue(editValue);
377
            value = source.getEditableValue();
377
			setErrorText(editor.getErrorMessage());
378
        }
378
		}
379
        return value;
379
		return editor;
380
    }
380
	}
381
381
382
    /* (non-Javadoc)
382
	/**
383
     * Method declared on IPropertySheetEntry.
383
	 * Returns the edit value for the object at the given index.
384
     */
384
	 * 
385
    public String getErrorText() {
385
	 * @param index
386
        return errorText;
386
	 *            the value object index
387
    }
387
	 * @return the edit value for the object at the given index
388
388
	 */
389
    /* (non-Javadoc)
389
	protected Object getEditValue(int index) {
390
     * Method declared on IPropertySheetEntry.
390
		Object value = values[index];
391
     */
391
		IPropertySource source = getPropertySource(value);
392
    public String getFilters()[] {
392
		if (source != null) {
393
        return descriptor.getFilterFlags();
393
			value = source.getEditableValue();
394
    }
394
		}
395
395
		return value;
396
    /* (non-Javadoc)
396
	}
397
     * Method declared on IPropertySheetEntry.
397
398
     */
398
	/*
399
    public Object getHelpContextIds() {
399
	 * (non-Javadoc) Method declared on IPropertySheetEntry.
400
        return descriptor.getHelpContextIds();
400
	 */
401
    }
401
	public String getErrorText() {
402
402
		return errorText;
403
    /* (non-Javadoc)
403
	}
404
     * Method declared on IPropertySheetEntry.
404
405
     */
405
	/*
406
    public Image getImage() {
406
	 * (non-Javadoc) Method declared on IPropertySheetEntry.
407
        ILabelProvider provider = descriptor.getLabelProvider();
407
	 */
408
        if (provider == null)
408
	public String getFilters()[] {
409
            return null;
409
		return descriptor.getFilterFlags();
410
        return provider.getImage(editValue);
410
	}
411
    }
411
412
412
	/*
413
    /**
413
	 * (non-Javadoc) Method declared on IPropertySheetEntry.
414
     * Returns an property source for the given object.
414
	 */
415
     *
415
	public Object getHelpContextIds() {
416
     * @param object an object for which to obtain a property source or
416
		return descriptor.getHelpContextIds();
417
     *  <code>null</code> if a property source is not available
417
	}
418
     * @return an property source for the given object
418
419
     */
419
	/*
420
    private IPropertySource getPropertySource(Object object) {
420
	 * (non-Javadoc) Method declared on IPropertySheetEntry.
421
        if (sources.containsKey(object))
421
	 */
422
            return (IPropertySource) sources.get(object);
422
	public Image getImage() {
423
423
		ILabelProvider provider = descriptor.getLabelProvider();
424
        IPropertySource result = null;
424
		if (provider == null)
425
        if (propertySourceProvider != null)
425
			return null;
426
            result = propertySourceProvider.getPropertySource(object);
426
		return provider.getImage(editValue);
427
        else if (object instanceof IPropertySource)
427
	}
428
            result = (IPropertySource) object;
428
429
        else if (object instanceof IAdaptable)
429
	/**
430
            result = (IPropertySource) ((IAdaptable) object)
430
	 * Returns an property source for the given object.
431
                    .getAdapter(IPropertySource.class);
431
	 * 
432
432
	 * @param object
433
        sources.put(object, result);
433
	 *            an object for which to obtain a property source or
434
        return result;
434
	 *            <code>null</code> if a property source is not available
435
    }
435
	 * @return an property source for the given object
436
436
	 */
437
    /* (non-Javadoc)
437
	private IPropertySource getPropertySource(Object object) {
438
     * Method declared on IPropertySheetEntry.
438
		if (sources.containsKey(object))
439
     */
439
			return (IPropertySource) sources.get(object);
440
    public String getValueAsString() {
440
441
        if (editValue == null)
441
		IPropertySource result = null;
442
            return "";//$NON-NLS-1$
442
		if (propertySourceProvider != null)
443
        ILabelProvider provider = descriptor.getLabelProvider();
443
			result = propertySourceProvider.getPropertySource(object);
444
        if (provider == null)
444
		else if (object instanceof IPropertySource)
445
            return editValue.toString();
445
			result = (IPropertySource) object;
446
        String text = provider.getText(editValue);
446
		else if (object instanceof IAdaptable)
447
        if(text == null)
447
			result = (IPropertySource) ((IAdaptable) object)
448
        	return "";//$NON-NLS-1$
448
					.getAdapter(IPropertySource.class);
449
        return text;
449
450
    }
450
		sources.put(object, result);
451
451
		return result;
452
    /**
452
	}
453
     * Returns the value objects of this entry.
453
454
     * @return Object[]
454
	/*
455
     */
455
	 * (non-Javadoc) Method declared on IPropertySheetEntry.
456
    private Object[] getValues() {
456
	 */
457
        return values;
457
	public String getValueAsString() {
458
    }
458
		if (editValue == null)
459
459
			return "";//$NON-NLS-1$
460
    /* (non-Javadoc)
460
		ILabelProvider provider = descriptor.getLabelProvider();
461
     * Method declared on IPropertySheetEntry.
461
		if (provider == null)
462
     */
462
			return editValue.toString();
463
    public boolean hasChildEntries() {
463
		String text = provider.getText(editValue);
464
        if (childEntries != null && childEntries.length > 0)
464
		if (text == null)
465
            return true;
465
			return "";//$NON-NLS-1$
466
        // see if we could have entires if we were asked
466
		return text;
467
        return computeMergedPropertyDescriptors().size() > 0;
467
	}
468
    }
468
469
469
	/**
470
    /**
470
	 * Returns the value objects of this entry.
471
     * Update our child entries.
471
	 * 
472
     * This implementation tries to reuse child entries if possible 
472
	 * @return Object[]
473
     * (if the id of the new descriptor matches the descriptor id of the
473
	 */
474
     * old entry).
474
	private Object[] getValues() {
475
     */
475
		return values;
476
    private void refreshChildEntries() {
476
	}
477
        if (childEntries == null)
477
478
            // no children to refresh
478
	/*
479
            return;
479
	 * (non-Javadoc) Method declared on IPropertySheetEntry.
480
480
	 */
481
        // get the current descriptors
481
	public boolean hasChildEntries() {
482
        List descriptors = computeMergedPropertyDescriptors();
482
		if (childEntries != null && childEntries.length > 0)
483
483
			return true;
484
        // cache old entries by their descriptor id
484
		// see if we could have entires if we were asked
485
        Map entryCache = new HashMap(childEntries.length * 2 + 1);
485
		return computeMergedPropertyDescriptors().size() > 0;
486
        for (int i = 0; i < childEntries.length; i++) {
486
	}
487
            entryCache.put(childEntries[i].getDescriptor().getId(),
487
488
                    childEntries[i]);
488
	/**
489
        }
489
	 * Update our child entries. This implementation tries to reuse child
490
490
	 * entries if possible (if the id of the new descriptor matches the
491
        // create a list of entries to dispose
491
	 * descriptor id of the old entry).
492
        List entriesToDispose = new ArrayList(Arrays.asList(childEntries));
492
	 */
493
493
	private void refreshChildEntries() {
494
        // rebuild child entries using old when possible
494
		if (childEntries == null)
495
        childEntries = new PropertySheetEntry[descriptors.size()];
495
			// no children to refresh
496
        boolean entriesChanged = descriptors.size() != entryCache.size();
496
			return;
497
        for (int i = 0; i < descriptors.size(); i++) {
497
498
            IPropertyDescriptor d = (IPropertyDescriptor) descriptors.get(i);
498
		// get the current descriptors
499
            // see if we have an entry matching this descriptor
499
		List descriptors = computeMergedPropertyDescriptors();
500
            PropertySheetEntry entry = (PropertySheetEntry) entryCache.get(d
500
501
                    .getId());
501
		// cache old entries by their descriptor id
502
            if (entry != null) {
502
		Map entryCache = new HashMap(childEntries.length * 2 + 1);
503
                // reuse old entry
503
		for (int i = 0; i < childEntries.length; i++) {
504
                entry.setDescriptor(d);
504
			entryCache.put(childEntries[i].getDescriptor().getId(),
505
                entriesToDispose.remove(entry);
505
					childEntries[i]);
506
            } else {
506
		}
507
                // create new entry
507
508
                entry = new PropertySheetEntry();
508
		// create a list of entries to dispose
509
                entry.setDescriptor(d);
509
		List entriesToDispose = new ArrayList(Arrays.asList(childEntries));
510
                entry.setParent(this);
510
511
                entry.setPropertySourceProvider(propertySourceProvider);
511
		// rebuild child entries using old when possible
512
                entriesChanged = true;
512
		childEntries = new PropertySheetEntry[descriptors.size()];
513
            }
513
		boolean entriesChanged = descriptors.size() != entryCache.size();
514
            entry.refreshValues();
514
		for (int i = 0; i < descriptors.size(); i++) {
515
            childEntries[i] = entry;
515
			IPropertyDescriptor d = (IPropertyDescriptor) descriptors.get(i);
516
        }
516
			// see if we have an entry matching this descriptor
517
517
			PropertySheetEntry entry = (PropertySheetEntry) entryCache.get(d
518
        if (entriesChanged)
518
					.getId());
519
            fireChildEntriesChanged();
519
			if (entry != null) {
520
520
				// reuse old entry
521
        //Dispose of entries which are no longer needed		
521
				entry.setDescriptor(d);
522
        for (int i = 0; i < entriesToDispose.size(); i++) {
522
				entriesToDispose.remove(entry);
523
            ((IPropertySheetEntry) entriesToDispose.get(i)).dispose();
523
			} else {
524
        }
524
				// create new entry
525
    }
525
				entry = createChildEntry();
526
526
				entry.setDescriptor(d);
527
    /**
527
				entry.setParent(this);
528
     * Refresh the entry tree from the root down
528
				entry.setPropertySourceProvider(propertySourceProvider);
529
     */
529
				entriesChanged = true;
530
    private void refreshFromRoot() {
530
			}
531
        if (parent == null)
531
			entry.refreshValues();
532
            refreshChildEntries();
532
			childEntries[i] = entry;
533
        else
533
		}
534
            parent.refreshFromRoot();
534
535
    }
535
		if (entriesChanged)
536
536
			fireChildEntriesChanged();
537
    /**
537
538
     * Update our value objects.
538
		// Dispose of entries which are no longer needed
539
     * We ask our parent for the property values based on
539
		for (int i = 0; i < entriesToDispose.size(); i++) {
540
     * our descriptor.
540
			((IPropertySheetEntry) entriesToDispose.get(i)).dispose();
541
     */
541
		}
542
    private void refreshValues() {
542
	}
543
        // get our parent's value objects
543
544
        Object[] currentSources = parent.getValues();
544
	/**
545
545
	 * Refresh the entry tree from the root down
546
        // loop through the objects getting our property value from each
546
	 */
547
        Object[] newValues = new Object[currentSources.length];
547
	private void refreshFromRoot() {
548
        for (int i = 0; i < currentSources.length; i++) {
548
		if (parent == null)
549
            IPropertySource source = parent
549
			refreshChildEntries();
550
                    .getPropertySource(currentSources[i]);
550
		else
551
            newValues[i] = source.getPropertyValue(descriptor.getId());
551
			parent.refreshFromRoot();
552
        }
552
	}
553
553
554
        // set our new values
554
	/**
555
        setValues(newValues);
555
	 * Update our value objects. We ask our parent for the property values based
556
    }
556
	 * on our descriptor.
557
557
	 */
558
    /* (non-Javadoc)
558
	private void refreshValues() {
559
     * Method declared on IPropertySheetEntry.
559
		// get our parent's value objects
560
     */
560
		Object[] currentSources = parent.getValues();
561
    public void removePropertySheetEntryListener(
561
562
            IPropertySheetEntryListener listener) {
562
		// loop through the objects getting our property value from each
563
        listeners.remove(listener);
563
		Object[] newValues = new Object[currentSources.length];
564
    }
564
		for (int i = 0; i < currentSources.length; i++) {
565
565
			IPropertySource source = parent
566
    /* (non-Javadoc)
566
					.getPropertySource(currentSources[i]);
567
     * Method declared on IPropertySheetEntry.
567
			newValues[i] = source.getPropertyValue(descriptor.getId());
568
     */
568
		}
569
    public void resetPropertyValue() {
569
570
        if (parent == null)
570
		// set our new values
571
            // root does not have a default value
571
		setValues(newValues);
572
            return;
572
	}
573
573
574
        //	Use our parent's values to reset our values.
574
	/*
575
        boolean change = false;
575
	 * (non-Javadoc) Method declared on IPropertySheetEntry.
576
        Object[] objects = parent.getValues();
576
	 */
577
        for (int i = 0; i < objects.length; i++) {
577
	public void removePropertySheetEntryListener(
578
            IPropertySource source = getPropertySource(objects[i]);
578
			IPropertySheetEntryListener listener) {
579
            if (source.isPropertySet(descriptor.getId())) {
579
		listeners.remove(listener);
580
                // fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=21756
580
	}
581
                if (source instanceof IPropertySource2) {
581
582
                    IPropertySource2 extendedSource = (IPropertySource2) source;
582
	/*
583
                    // continue with next if property is not resettable
583
	 * (non-Javadoc) Method declared on IPropertySheetEntry.
584
                    if (!extendedSource
584
	 */
585
                            .isPropertyResettable(descriptor.getId()))
585
	public void resetPropertyValue() {
586
                        continue;
586
		if (parent == null)
587
                }
587
			// root does not have a default value
588
                source.resetPropertyValue(descriptor.getId());
588
			return;
589
                change = true;
589
590
            }
590
		// Use our parent's values to reset our values.
591
        }
591
		boolean change = false;
592
        if (change)
592
		Object[] objects = parent.getValues();
593
            refreshFromRoot();
593
		for (int i = 0; i < objects.length; i++) {
594
    }
594
			IPropertySource source = getPropertySource(objects[i]);
595
595
			if (source.isPropertySet(descriptor.getId())) {
596
    /**
596
				// fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=21756
597
     * Set the descriptor.
597
				if (source instanceof IPropertySource2) {
598
     * @param newDescriptor
598
					IPropertySource2 extendedSource = (IPropertySource2) source;
599
     */
599
					// continue with next if property is not resettable
600
    private void setDescriptor(IPropertyDescriptor newDescriptor) {
600
					if (!extendedSource
601
        // if our descriptor is changing, we have to get rid
601
							.isPropertyResettable(descriptor.getId()))
602
        // of our current editor if there is one
602
						continue;
603
        if (descriptor != newDescriptor && editor != null) {
603
				}
604
            editor.dispose();
604
				source.resetPropertyValue(descriptor.getId());
605
            editor = null;
605
				change = true;
606
        }
606
			}
607
        descriptor = newDescriptor;
607
		}
608
    }
608
		if (change)
609
609
			refreshFromRoot();
610
    /*
610
	}
611
     * Set the error text.  This should be set to null when
611
612
     * the current value is valid, otherwise it should be
612
	/**
613
     * set to a error string
613
	 * Set the descriptor.
614
     */
614
	 * 
615
    private void setErrorText(String newErrorText) {
615
	 * @param newDescriptor
616
        errorText = newErrorText;
616
	 */
617
        // inform listeners
617
	private void setDescriptor(IPropertyDescriptor newDescriptor) {
618
        fireErrorMessageChanged();
618
		// if our descriptor is changing, we have to get rid
619
    }
619
		// of our current editor if there is one
620
620
		if (descriptor != newDescriptor && editor != null) {
621
    /**
621
			editor.dispose();
622
     * Sets the parent of the entry to be propertySheetEntry.
622
			editor = null;
623
     * @param propertySheetEntry
623
		}
624
     */
624
		descriptor = newDescriptor;
625
    private void setParent(PropertySheetEntry propertySheetEntry) {
625
	}
626
        parent = propertySheetEntry;
626
627
    }
627
	/*
628
628
	 * Set the error text. This should be set to null when the current value is
629
    /**
629
	 * valid, otherwise it should be set to a error string
630
     * Sets a property source provider for this entry. 
630
	 */
631
     * This provider is used to obtain an <code>IPropertySource</code>
631
	private void setErrorText(String newErrorText) {
632
     * for each of this entries objects. If no provider is
632
		errorText = newErrorText;
633
     * set then a default provider is used.
633
		// inform listeners
634
     * @param provider IPropertySourceProvider
634
		fireErrorMessageChanged();
635
     */
635
	}
636
    public void setPropertySourceProvider(IPropertySourceProvider provider) {
636
637
        propertySourceProvider = provider;
637
	/**
638
    }
638
	 * Sets the parent of the entry to be propertySheetEntry.
639
639
	 * 
640
    /**
640
	 * @param propertySheetEntry
641
     * Set the value for this entry.
641
	 */
642
     * <p>
642
	private void setParent(PropertySheetEntry propertySheetEntry) {
643
     * We set the given value as the value for all our value objects.
643
		parent = propertySheetEntry;
644
     * We then call our parent to update the property we represent
644
	}
645
     * with the given value.
645
646
     * We then trigger a model refresh.
646
	/**
647
     * <p>
647
	 * Sets a property source provider for this entry. This provider is used to
648
     *
648
	 * obtain an <code>IPropertySource</code> for each of this entries
649
     * @param newValue the new value
649
	 * objects. If no provider is set then a default provider is used.
650
     */
650
	 * 
651
    private void setValue(Object newValue) {
651
	 * @param provider
652
        // Set the value
652
	 *            IPropertySourceProvider
653
        for (int i = 0; i < values.length; i++) {
653
	 */
654
            values[i] = newValue;
654
	public void setPropertySourceProvider(IPropertySourceProvider provider) {
655
        }
655
		propertySourceProvider = provider;
656
656
	}
657
        // Inform our parent
657
658
        parent.valueChanged(this);
658
	/**
659
659
	 * Set the value for this entry.
660
        // Refresh the model
660
	 * <p>
661
        refreshFromRoot();
661
	 * We set the given value as the value for all our value objects. We then
662
    }
662
	 * call our parent to update the property we represent with the given value.
663
663
	 * We then trigger a model refresh.
664
    /** 
664
	 * <p>
665
     * The <code>PropertySheetEntry</code> implmentation of this
665
	 * 
666
     * method declared on<code>IPropertySheetEntry</code> will
666
	 * @param newValue
667
     * obtain an editable value for the given objects and update
667
	 *            the new value
668
     * the child entries.
668
	 */
669
     * <p>
669
	private void setValue(Object newValue) {
670
     * Updating the child entries will typically call this method
670
		// Set the value
671
     * on the child entries and thus the entire entry tree is updated
671
		for (int i = 0; i < values.length; i++) {
672
     * </p>
672
			values[i] = newValue;
673
     * @param objects the new values for this entry
673
		}
674
     */
674
675
    public void setValues(Object[] objects) {
675
		// Inform our parent
676
        values = objects;
676
		parent.valueChanged(this);
677
        sources = new HashMap(values.length * 2 + 1);
677
678
678
		// Refresh the model
679
        if (values.length == 0)
679
		refreshFromRoot();
680
            editValue = null;
680
	}
681
        else {
681
682
            // set the first value object as the entry's value
682
	/**
683
            Object newValue = values[0];
683
	 * The <code>PropertySheetEntry</code> implmentation of this method
684
684
	 * declared on<code>IPropertySheetEntry</code> will obtain an editable
685
            // see if we should convert the value to an editable value
685
	 * value for the given objects and update the child entries.
686
            IPropertySource source = getPropertySource(newValue);
686
	 * <p>
687
            if (source != null)
687
	 * Updating the child entries will typically call this method on the child
688
                newValue = source.getEditableValue();
688
	 * entries and thus the entire entry tree is updated
689
            editValue = newValue;
689
	 * </p>
690
        }
690
	 * 
691
691
	 * @param objects
692
        // update our child entries
692
	 *            the new values for this entry
693
        refreshChildEntries();
693
	 */
694
694
	public void setValues(Object[] objects) {
695
        // inform listeners that our value changed
695
		values = objects;
696
        fireValueChanged();
696
		sources = new HashMap(values.length * 2 + 1);
697
    }
697
698
698
		if (values.length == 0)
699
    /**
699
			editValue = null;
700
     * The value of the given child entry has changed.
700
		else {
701
     * Therefore we must set this change into our value objects.
701
			// set the first value object as the entry's value
702
     * <p>
702
			Object newValue = values[0];
703
     * We must inform our parent so that it can update its value objects
703
704
     * </p>
704
			// see if we should convert the value to an editable value
705
     * <p>
705
			IPropertySource source = getPropertySource(newValue);
706
     * Subclasses may override to set the property value in some
706
			if (source != null)
707
     * custom way.
707
				newValue = source.getEditableValue();
708
     * </p>
708
			editValue = newValue;
709
     *
709
		}
710
     * @param child the child entry that changed its value
710
711
     */
711
		// update our child entries
712
    protected void valueChanged(PropertySheetEntry child) {
712
		refreshChildEntries();
713
        for (int i = 0; i < values.length; i++) {
713
714
            IPropertySource source = getPropertySource(values[i]);
714
		// inform listeners that our value changed
715
            source.setPropertyValue(child.getDescriptor().getId(), child
715
		fireValueChanged();
716
                    .getEditValue(i));
716
	}
717
        }
717
718
718
	/**
719
        // inform our parent
719
	 * The value of the given child entry has changed. Therefore we must set
720
        if (parent != null)
720
	 * this change into our value objects.
721
            parent.valueChanged(this);
721
	 * <p>
722
    }
722
	 * We must inform our parent so that it can update its value objects
723
	 * </p>
724
	 * <p>
725
	 * Subclasses may override to set the property value in some custom way.
726
	 * </p>
727
	 * 
728
	 * @param child
729
	 *            the child entry that changed its value
730
	 */
731
	protected void valueChanged(PropertySheetEntry child) {
732
		for (int i = 0; i < values.length; i++) {
733
			IPropertySource source = getPropertySource(values[i]);
734
			source.setPropertyValue(child.getDescriptor().getId(), child
735
					.getEditValue(i));
736
		}
737
738
		// inform our parent
739
		if (parent != null)
740
			parent.valueChanged(this);
741
	}
723
}
742
}

Return to bug 24694