1 /*******************************************************************************
2  * Copyright (c) 2008, 2015 Matthew Hall and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     Matthew Hall - initial API and implementation (bug 194734)
13  *     Matthew Hall - bug 195222, 247997, 261843, 264307
14  ******************************************************************************/
15 
16 package org.eclipse.core.databinding.beans.typed;
17 
18 import java.beans.PropertyChangeEvent;
19 import java.beans.PropertyDescriptor;
20 import java.util.ArrayList;
21 import java.util.List;
22 
23 import org.eclipse.core.databinding.beans.IBeanListProperty;
24 import org.eclipse.core.databinding.beans.IBeanMapProperty;
25 import org.eclipse.core.databinding.beans.IBeanSetProperty;
26 import org.eclipse.core.databinding.beans.IBeanValueProperty;
27 import org.eclipse.core.databinding.property.list.IListProperty;
28 import org.eclipse.core.databinding.property.map.IMapProperty;
29 import org.eclipse.core.databinding.property.set.ISetProperty;
30 import org.eclipse.core.databinding.property.value.IValueProperty;
31 import org.eclipse.core.internal.databinding.beans.AnonymousPojoListProperty;
32 import org.eclipse.core.internal.databinding.beans.AnonymousPojoMapProperty;
33 import org.eclipse.core.internal.databinding.beans.AnonymousPojoSetProperty;
34 import org.eclipse.core.internal.databinding.beans.AnonymousPojoValueProperty;
35 import org.eclipse.core.internal.databinding.beans.BeanPropertyHelper;
36 import org.eclipse.core.internal.databinding.beans.PojoListProperty;
37 import org.eclipse.core.internal.databinding.beans.PojoListPropertyDecorator;
38 import org.eclipse.core.internal.databinding.beans.PojoMapProperty;
39 import org.eclipse.core.internal.databinding.beans.PojoMapPropertyDecorator;
40 import org.eclipse.core.internal.databinding.beans.PojoSetProperty;
41 import org.eclipse.core.internal.databinding.beans.PojoSetPropertyDecorator;
42 import org.eclipse.core.internal.databinding.beans.PojoValueProperty;
43 import org.eclipse.core.internal.databinding.beans.PojoValuePropertyDecorator;
44 
45 /**
46  * A factory for creating properties for POJOs (plain old java objects) that
47  * conform to idea of an object with getters and setters but does not provide
48  * {@link PropertyChangeEvent property change events} on change. This factory is
49  * identical to {@link BeanProperties} except for this fact.
50  * <p>
51  * This class is a new version of the deprecated class with the same name in the
52  * parent package. The difference is that this class returns typed property
53  * objects. This class is located in its own package to be able to coexist with
54  * the old version while having the same name.
55  *
56  * @since 1.5
57  */
58 public class PojoProperties {
59 	/**
60 	 * Returns a value property for the given property name of an arbitrary bean
61 	 * class. Objects lacking the named property are treated the same as if the
62 	 * property always contains null.
63 	 *
64 	 * @param propertyName
65 	 *            the property name. May be nested e.g. "parent.name"
66 	 * @return a value property for the given property name of an arbitrary bean
67 	 *         class.
68 	 */
value(String propertyName)69 	public static <S, T> IBeanValueProperty<S, T> value(String propertyName) {
70 		return value(null, propertyName, null);
71 	}
72 
73 	/**
74 	 * Returns a value property for the given property name of an arbitrary bean
75 	 * class. Objects lacking the named property are treated the same as if the
76 	 * property always contains null.
77 	 *
78 	 * @param propertyName
79 	 *            the property name. May be nested e.g. "parent.name"
80 	 * @param valueType
81 	 *            the value type of the returned value property
82 	 * @return a value property for the given property name of an arbitrary bean
83 	 *         class.
84 	 */
value(String propertyName, Class<T> valueType)85 	public static <S, T> IBeanValueProperty<S, T> value(String propertyName, Class<T> valueType) {
86 		return value(null, propertyName, valueType);
87 	}
88 
89 	/**
90 	 * Returns a value property for the given property name of the given bean
91 	 * class.
92 	 *
93 	 * @param beanClass
94 	 *            the bean class
95 	 * @param propertyName
96 	 *            the property name. May be nested e.g. "parent.name"
97 	 * @return a value property for the given property name of the given bean
98 	 *         class.
99 	 */
value(Class<S> beanClass, String propertyName)100 	public static <S, E> IBeanValueProperty<S, E> value(Class<S> beanClass, String propertyName) {
101 		return value(beanClass, propertyName, null);
102 	}
103 
104 	/**
105 	 * Returns a value property for the given property name of the given bean
106 	 * class.
107 	 *
108 	 * @param beanClass
109 	 *            the bean class
110 	 * @param propertyName
111 	 *            the property name. May be nested e.g. "parent.name"
112 	 * @param valueType
113 	 *            the value type of the returned value property
114 	 * @return a value property for the given property name of the given bean
115 	 *         class.
116 	 */
117 	@SuppressWarnings("unchecked")
value(Class<S> beanClass, String propertyName, Class<T> valueType)118 	public static <S, T> IBeanValueProperty<S, T> value(Class<S> beanClass, String propertyName, Class<T> valueType) {
119 		String[] propertyNames = split(propertyName);
120 		if (propertyNames.length > 1)
121 			valueType = null;
122 
123 		IValueProperty<S, T> property;
124 		PropertyDescriptor propertyDescriptor;
125 		if (beanClass == null) {
126 			propertyDescriptor = null;
127 			property = new PojoValuePropertyDecorator<>(new AnonymousPojoValueProperty<>(propertyNames[0], valueType),
128 					null);
129 		} else {
130 			propertyDescriptor = BeanPropertyHelper.getPropertyDescriptor(beanClass, propertyNames[0]);
131 			property = new PojoValueProperty<>(propertyDescriptor, valueType);
132 		}
133 
134 		IBeanValueProperty<S, T> beanProperty = new PojoValuePropertyDecorator<>(property, propertyDescriptor);
135 		for (int i = 1; i < propertyNames.length; i++) {
136 			beanProperty = (IBeanValueProperty<S, T>) beanProperty.value(propertyNames[i]);
137 		}
138 		return beanProperty;
139 	}
140 
split(String propertyName)141 	private static String[] split(String propertyName) {
142 		if (propertyName.indexOf('.') == -1)
143 			return new String[] { propertyName };
144 		List<String> propertyNames = new ArrayList<>();
145 		int index;
146 		while ((index = propertyName.indexOf('.')) != -1) {
147 			propertyNames.add(propertyName.substring(0, index));
148 			propertyName = propertyName.substring(index + 1);
149 		}
150 		propertyNames.add(propertyName);
151 		return propertyNames.toArray(new String[propertyNames.size()]);
152 	}
153 
154 	/**
155 	 * Returns a value property array for the given property names of the given
156 	 * bean class.
157 	 *
158 	 * @param beanClass
159 	 *            the bean class
160 	 * @param propertyNames
161 	 *            array of property names. May be nested e.g. "parent.name"
162 	 * @return a value property array for the given property names of the given
163 	 *         bean class.
164 	 */
values(Class<S> beanClass, String... propertyNames)165 	public static <S, T> IBeanValueProperty<S, T>[] values(Class<S> beanClass,
166 			String... propertyNames) {
167 		@SuppressWarnings("unchecked")
168 		IBeanValueProperty<S, T>[] properties = (IBeanValueProperty<S, T>[]) new IBeanValueProperty<?, ?>[propertyNames.length];
169 		for (int i = 0; i < properties.length; i++)
170 			properties[i] = value(beanClass, propertyNames[i], null);
171 		return properties;
172 	}
173 
174 	/**
175 	 * Returns a value property array for the given property names of an
176 	 * arbitrary bean class.
177 	 *
178 	 * @param propertyNames
179 	 *            array of property names. May be nested e.g. "parent.name"
180 	 * @return a value property array for the given property names of the given
181 	 *         bean class.
182 	 */
values(String... propertyNames)183 	public static <S, T> IBeanValueProperty<S, T>[] values(String... propertyNames) {
184 		return values(null, propertyNames);
185 	}
186 
187 	/**
188 	 * Returns a set property for the given property name of an arbitrary bean
189 	 * class. Objects lacking the named property are treated the same as if the
190 	 * property always contains an empty set.
191 	 *
192 	 * @param propertyName
193 	 *            the property name
194 	 * @return a set property for the given property name of an arbitrary bean
195 	 *         class.
196 	 */
set(String propertyName)197 	public static <S, E> IBeanSetProperty<S, E> set(String propertyName) {
198 		return set(null, propertyName, null);
199 	}
200 
201 	/**
202 	 * Returns a set property for the given property name of an arbitrary bean
203 	 * class. Objects lacking the named property are treated the same as if the
204 	 * property always contains an empty set.
205 	 *
206 	 * @param propertyName
207 	 *            the property name
208 	 * @param elementType
209 	 *            the element type of the returned set property
210 	 * @return a set property for the given property name of an arbitrary bean
211 	 *         class.
212 	 */
set(String propertyName, Class<E> elementType)213 	public static <S, E> IBeanSetProperty<S, E> set(String propertyName, Class<E> elementType) {
214 		return set(null, propertyName, elementType);
215 	}
216 
217 	/**
218 	 * Returns a set property for the given property name of the given bean
219 	 * class.
220 	 *
221 	 * @param beanClass
222 	 *            the bean class
223 	 * @param propertyName
224 	 *            the property name
225 	 * @return a set property for the given property name of the given bean
226 	 *         class.
227 	 */
set(Class<S> beanClass, String propertyName)228 	public static <S, E> IBeanSetProperty<S, E> set(Class<S> beanClass, String propertyName) {
229 		return set(beanClass, propertyName, null);
230 	}
231 
232 	/**
233 	 * Returns a set property for the given property name of the given bean
234 	 * class.
235 	 *
236 	 * @param beanClass
237 	 *            the bean class
238 	 * @param propertyName
239 	 *            the property name
240 	 * @param elementType
241 	 *            the element type of the returned set property
242 	 * @return a set property for the given property name of the given bean
243 	 *         class.
244 	 */
set(Class<S> beanClass, String propertyName, Class<E> elementType)245 	public static <S, E> IBeanSetProperty<S, E> set(Class<S> beanClass, String propertyName, Class<E> elementType) {
246 		PropertyDescriptor propertyDescriptor;
247 		ISetProperty<S, E> property;
248 		if (beanClass == null) {
249 			propertyDescriptor = null;
250 			property = new AnonymousPojoSetProperty<>(propertyName, elementType);
251 		} else {
252 			propertyDescriptor = BeanPropertyHelper.getPropertyDescriptor(
253 					beanClass, propertyName);
254 			property = new PojoSetProperty<>(propertyDescriptor, elementType);
255 		}
256 		return new PojoSetPropertyDecorator<>(property, propertyDescriptor);
257 	}
258 
259 	/**
260 	 * Returns a list property for the given property name of an arbitrary bean
261 	 * class. Objects lacking the named property are treated the same as if the
262 	 * property always contains an empty list.
263 	 *
264 	 * @param propertyName
265 	 *            the property name
266 	 * @return a list property for the given property name of an arbitrary bean
267 	 *         class.
268 	 */
list(String propertyName)269 	public static <S, E> IBeanListProperty<S, E> list(String propertyName) {
270 		return list(null, propertyName, null);
271 	}
272 
273 	/**
274 	 * Returns a list property for the given property name of an arbitrary bean
275 	 * class. Objects lacking the named property are treated the same as if the
276 	 * property always contains an empty list.
277 	 *
278 	 * @param propertyName
279 	 *            the property name
280 	 * @param elementType
281 	 *            the element type of the returned list property
282 	 * @return a list property for the given property name of the given bean
283 	 *         class.
284 	 */
list(String propertyName, Class<E> elementType)285 	public static <S, E> IBeanListProperty<S, E> list(String propertyName, Class<E> elementType) {
286 		return list(null, propertyName, elementType);
287 	}
288 
289 	/**
290 	 * Returns a list property for the given property name of the given bean
291 	 * class.
292 	 *
293 	 * @param beanClass
294 	 *            the bean class
295 	 * @param propertyName
296 	 *            the property name
297 	 * @return a list property for the given property name of the given bean
298 	 *         class.
299 	 */
list(Class<S> beanClass, String propertyName)300 	public static <S, E> IBeanListProperty<S, E> list(Class<S> beanClass, String propertyName) {
301 		return list(beanClass, propertyName, null);
302 	}
303 
304 	/**
305 	 * Returns a list property for the given property name of the given bean
306 	 * class.
307 	 *
308 	 * @param beanClass
309 	 *            the bean class
310 	 * @param propertyName
311 	 *            the property name
312 	 * @param elementType
313 	 *            the element type of the returned list property
314 	 * @return a list property for the given property name of the given bean
315 	 *         class.
316 	 */
list(Class<S> beanClass, String propertyName, Class<E> elementType)317 	public static <S, E> IBeanListProperty<S, E> list(Class<S> beanClass, String propertyName, Class<E> elementType) {
318 		PropertyDescriptor propertyDescriptor;
319 		IListProperty<S, E> property;
320 		if (beanClass == null) {
321 			propertyDescriptor = null;
322 			property = new AnonymousPojoListProperty<>(propertyName, elementType);
323 		} else {
324 			propertyDescriptor = BeanPropertyHelper.getPropertyDescriptor(
325 					beanClass, propertyName);
326 			property = new PojoListProperty<>(propertyDescriptor, elementType);
327 		}
328 		return new PojoListPropertyDecorator<>(property, propertyDescriptor);
329 	}
330 
331 	/**
332 	 * Returns a map property for the given property name of an arbitrary bean
333 	 * class. Objects lacking the named property are treated the same as if the
334 	 * property always contains an empty map.
335 	 *
336 	 * @param propertyName
337 	 *            the property name
338 	 * @return a map property for the given property name of an arbitrary bean
339 	 *         class.
340 	 */
map(String propertyName)341 	public static <S, K, V> IBeanMapProperty<S, K, V> map(String propertyName) {
342 		return map(null, propertyName, null, null);
343 	}
344 
345 	/**
346 	 * Returns a map property for the given property name of an arbitrary bean
347 	 * class. Objects lacking the named property are treated the same as if the
348 	 * property always contains an empty map.
349 	 *
350 	 * @param propertyName
351 	 *            the property name
352 	 * @param keyType
353 	 *            the key type for the returned map property
354 	 * @param valueType
355 	 *            the value type for the returned map property
356 	 * @return a map property for the given property name of an arbitrary bean
357 	 *         class.
358 	 */
map(String propertyName, Class<K> keyType, Class<V> valueType)359 	public static <S, K, V> IBeanMapProperty<S, K, V> map(String propertyName, Class<K> keyType, Class<V> valueType) {
360 		return map(null, propertyName, keyType, valueType);
361 	}
362 
363 	/**
364 	 * Returns a map property for the given property name of the given bean
365 	 * class.
366 	 *
367 	 * @param beanClass
368 	 *            the bean class
369 	 * @param propertyName
370 	 *            the property name
371 	 * @return a map property for the given property name of the given bean
372 	 *         class.
373 	 */
map(Class<S> beanClass, String propertyName)374 	public static <S, K, V> IBeanMapProperty<S, K, V> map(Class<S> beanClass, String propertyName) {
375 		return map(beanClass, propertyName, null, null);
376 	}
377 
378 	/**
379 	 * Returns a map property for the given property name of the given bean
380 	 * class.
381 	 *
382 	 * @param beanClass
383 	 *            the bean class
384 	 * @param propertyName
385 	 *            the property name
386 	 * @param keyType
387 	 *            the key type of the returned map property
388 	 * @param valueType
389 	 *            the value type of the returned map property
390 	 * @return a map property for the given property name of the given bean
391 	 *         class.
392 	 */
map(Class<S> beanClass, String propertyName, Class<K> keyType, Class<V> valueType)393 	public static <S, K, V> IBeanMapProperty<S, K, V> map(Class<S> beanClass, String propertyName, Class<K> keyType,
394 			Class<V> valueType) {
395 		PropertyDescriptor propertyDescriptor;
396 		IMapProperty<S, K, V> property;
397 		if (beanClass == null) {
398 			propertyDescriptor = null;
399 			property = new AnonymousPojoMapProperty<>(propertyName, keyType, valueType);
400 		} else {
401 			propertyDescriptor = BeanPropertyHelper.getPropertyDescriptor(beanClass, propertyName);
402 			property = new PojoMapProperty<>(propertyDescriptor, keyType, valueType);
403 		}
404 		return new PojoMapPropertyDecorator<>(property, propertyDescriptor);
405 	}
406 }
407