View Javadoc

1   /*
2    * This file is a part of CAST project.
3    * (c) Copyright 2007, AGH University of Science & Technology
4    * https://caribou.iisg.agh.edu.pl/trac/cast
5    *
6    * Licensed under the Eclipse Public License, Version 1.0 (the "License").
7    * You may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    * http://www.eclipse.org/legal/epl-v10.html
10   */
11  /*
12   * File: ModelElement.java
13   * Created: 2007-00-00
14   * Author: awos
15   * $Id: ModelElement.java 3020 2009-07-17 14:41:19Z kpietak $
16   */
17  
18  package pl.edu.agh.cast.model.visual.backward;
19  
20  import java.beans.PropertyChangeEvent;
21  import java.beans.PropertyChangeListener;
22  import java.io.Serializable;
23  import java.util.ArrayList;
24  import java.util.Collection;
25  import java.util.LinkedList;
26  import java.util.List;
27  import java.util.SortedMap;
28  import java.util.TreeMap;
29  
30  import pl.edu.agh.cast.backward.resources.xml.AttributeValuesConverter;
31  import pl.edu.agh.cast.data.model.property.IPropertyChangeProvider;
32  import pl.edu.agh.cast.data.model.property.PropertyChangeProviderHelper;
33  import pl.edu.agh.cast.model.attributes.Attribute;
34  import pl.edu.agh.cast.model.attributes.AttributeManager;
35  import pl.edu.agh.cast.model.attributes.AttributeMergePolicy;
36  import pl.edu.agh.cast.model.attributes.AttributeValue;
37  import pl.edu.agh.cast.model.mapper.Mappable;
38  import pl.edu.agh.cast.util.Messages;
39  
40  import com.thoughtworks.xstream.annotations.XStreamAlias;
41  import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
42  import com.thoughtworks.xstream.annotations.XStreamConverter;
43  import com.thoughtworks.xstream.annotations.XStreamOmitField;
44  
45  /**
46   * An abstract class for a visual model element. Provides implementation for property change support.
47   *
48   * @author AGH CAST Team
49   */
50  public abstract class ModelElement implements Serializable, IPropertyChangeProvider, AttributeValueContainer,
51          PropertyChangeListener {
52  
53  	private static final long serialVersionUID = 4933221188249730849L;
54  
55  	/**
56  	 * Id of an event informing of attribute value changes in model elements. In the {@link PropertyChangeEvent}, old
57  	 * value is of type {@link AttributeValue}, and new value is the {@link AttributeValue#getValue()} of the new value.
58  	 */
59  	public static final String ATTRIBUTE_CHANGE = "ModelElement.attributeValue"; //$NON-NLS-1$
60  
61  	/**
62  	 *
63  	 */
64  	@XStreamAlias("attributeValues")
65  	@XStreamConverter(AttributeValuesConverter.class)
66  	protected SortedMap<Attribute, AttributeValue> attributeValues;
67  
68  	@XStreamAlias("mid")
69  	@XStreamAsAttribute
70  	private long mid;
71  
72  	@XStreamAlias("saved")
73  	@XStreamAsAttribute
74  	private boolean saved;
75  
76  	@XStreamOmitField
77  	private transient PropertyChangeProviderHelper pcpHelper;
78  
79  	/**
80  	 * Initializes new instance.
81  	 */
82  	protected ModelElement() {
83  		pcpHelper = new PropertyChangeProviderHelper(this);
84  		attributeValues = new TreeMap<Attribute, AttributeValue>();
85  	}
86  
87  	/**
88  	 * {@inheritDoc}
89  	 *
90  	 * @see pl.edu.agh.cast.model.visual.backward.AttributeValueContainer#getAttributeManager()
91  	 */
92  	public abstract AttributeManager getAttributeManager();
93  
94  	public long getMid() {
95  		return mid;
96  	}
97  
98  	public void setMid(long mid) {
99  		this.mid = mid;
100 	}
101 
102 	public boolean isSaved() {
103 		return saved;
104 	}
105 
106 	public void setSaved(boolean saved) {
107 		this.saved = saved;
108 	}
109 
110 	protected Object readResolve() {
111 		pcpHelper = new PropertyChangeProviderHelper(this);
112 		return this;
113 	}
114 
115 	// BEGIN Property change support
116 
117 	/**
118 	 * {@inheritDoc}
119 	 *
120 	 * @see pl.edu.agh.cast.data.model.property.IPropertyChangeProvider#addPropertyChangeListener(java.beans.PropertyChangeListener)
121 	 */
122 	public void addPropertyChangeListener(PropertyChangeListener l) {
123 		pcpHelper.addPropertyChangeListener(l);
124 	}
125 
126 	/**
127 	 * {@inheritDoc}
128 	 *
129 	 * @see pl.edu.agh.cast.data.model.property.IPropertyChangeProvider
130 	 *      #removePropertyChangeListener(java.beans.PropertyChangeListener)
131 	 */
132 	public void removePropertyChangeListener(PropertyChangeListener l) {
133 		pcpHelper.removePropertyChangeListener(l);
134 	}
135 
136 	/**
137 	 * Notifies about property change.
138 	 *
139 	 * @param property
140 	 *            name of the property
141 	 * @param oldValue
142 	 *            old value of the property
143 	 * @param newValue
144 	 *            new value of the property
145 	 */
146 	protected void firePropertyChange(String property, Object oldValue, Object newValue) {
147 		pcpHelper.firePropertyChange(property, oldValue, newValue);
148 	}
149 
150 	// END Property change support
151 
152 	// BEGIN Attribute handling
153 
154 	/**
155 	 * {@inheritDoc}
156 	 *
157 	 * @see pl.edu.agh.cast.model.visual.backward.AttributeValueContainer#getAttributeValue(java.lang.String)
158 	 */
159 	public AttributeValue getAttributeValue(String name) {
160 		Attribute attribute = getAttributeManager().getAttribute(name);
161 		if (attributeValues.containsKey(attribute)) {
162 			return attributeValues.get(attribute);
163 		} else {
164 			// return a temporary value object with attribute's default value
165 			return new AttributeValue(attribute, attribute.getDefaultValue());
166 		}
167 	}
168 
169 	/**
170 	 * Checks if an attribute value may be set.
171 	 *
172 	 * @param name
173 	 *            name of the attribute
174 	 * @return <code>true</code> if attribute value may be set
175 	 */
176 	public boolean isAttributeSettable(String name) {
177 		Attribute attribute = getAttributeManager().getAttribute(name);
178 		return attribute.isEditable() || getAttributeValue(name).getValue() == null;
179 	}
180 
181 	/**
182 	 * {@inheritDoc}
183 	 *
184 	 * @see pl.edu.agh.cast.model.visual.backward.AttributeValueContainer#setAttributeValue(java.lang.String, java.lang.Object)
185 	 */
186 	public void setAttributeValue(String name, Object newValue) {
187 		setAttributeValue(name, newValue, null);
188 	}
189 
190 	/**
191 	 * {@inheritDoc}
192 	 *
193 	 * @see pl.edu.agh.cast.model.visual.backward.AttributeValueContainer#setAttributeValue(java.lang.String, java.lang.Object,
194 	 *      pl.edu.agh.cast.model.attributes.AttributeMergePolicy)
195 	 */
196 	public void setAttributeValue(String name, Object newValue, AttributeMergePolicy policy) {
197 
198 		Attribute attribute = getAttributeManager().getAttribute(name);
199 
200 		if (!isAttributeSettable(name)) {
201 			// non-editable attributes can be assigned values only once
202 			// this is more less the same as assigning to a null
203 			throw new UnsupportedOperationException(Messages.ModelElementPropertyHolder_0);
204 		} else {
205 			Object oldVal = getAttributeValue(name);
206 			Object newVal = newValue;
207 			if (policy != null) {
208 				newVal = policy.mergeValues(attribute, oldVal == null ? null : ((AttributeValue)oldVal)
209 				        .getValue(), newValue);
210 			}
211 			attributeValues.put(attribute, new AttributeValue(attribute, newVal));
212 
213 			// XXX [tmilos] inconsistent types:
214 			// (oldValue.getClass() == AttributeValue.class)
215 			// (newValue.getClass() != AttributeValue.class)
216 			firePropertyChange(ATTRIBUTE_CHANGE, oldVal, newVal);
217 		}
218 	}
219 
220 	/**
221 	 * {@inheritDoc}
222 	 *
223 	 * @see pl.edu.agh.cast.model.visual.backward.AttributeValueContainer#isAttributeEditable(java.lang.String)
224 	 */
225 	public boolean isAttributeEditable(String attributeName) {
226 		Attribute attribute = getAttributeManager().getAttribute(attributeName);
227 		return attribute.isEditable();
228 	}
229 
230 	/**
231 	 * {@inheritDoc}
232 	 *
233 	 * @see pl.edu.agh.cast.model.visual.backward.AttributeValueContainer#getAllValues()
234 	 */
235 	public List<AttributeValue> getAllValues() {
236 		Collection<Attribute> attributes = getAttributeManager().getAttributes();
237 
238 		// Update local property value collection
239 		for (Attribute attribute : attributes) {
240 			if (!attributeValues.containsKey(attribute)) {
241 				// populate unset attributes with default values
242 				setAttributeValue(attribute.getName(), getAttributeValue(attribute.getName()).getValue());
243 			}
244 		}
245 
246 		// Remove properties which names are not registered
247 		Collection<Attribute> keySet = new LinkedList<Attribute>(attributeValues.keySet());
248 		for (Attribute setAttribute : keySet) {
249 			if (!attributes.contains(setAttribute)) {
250 				removePropertyValue(setAttribute.getName());
251 			}
252 		}
253 
254 		return new ArrayList<AttributeValue>(attributeValues.values());
255 	}
256 
257 	/**
258 	 * {@inheritDoc}
259 	 *
260 	 * @see pl.edu.agh.cast.model.visual.backward.AttributeValueContainer#removePropertyValue(java.lang.String)
261 	 */
262 	public AttributeValue removePropertyValue(String name) {
263 		// cannot use _attributeManager.getAttribute(name), because the property
264 		// can be already unregistered
265 		for (AttributeValue value : attributeValues.values()) {
266 			if (value.getAttribute().getName().equals(name)) {
267 				return attributeValues.remove(value.getAttribute());
268 			}
269 		}
270 		return null;
271 	}
272 
273 	/**
274 	 * Copies attributes present in current model element to another model element.
275 	 *
276 	 * @param target
277 	 *            {@link AttributeValueContainer} to copy to
278 	 */
279 	protected void copyAttributes(AttributeValueContainer target) {
280 		for (AttributeValue value : attributeValues.values()) {
281 			if (isAttributeSettable(value.getAttribute().getName())) {
282 				target.setAttributeValue(value.getAttribute().getName(), value.getValue());
283 			}
284 		}
285 	}
286 
287 	/**
288 	 * {@inheritDoc}
289 	 *
290 	 * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
291 	 */
292 	public void propertyChange(PropertyChangeEvent evt) {
293 		if (evt.getNewValue() == null && AttributeManager.ATTRIBUTE_REGISTRATION_STATUS.equals(evt.getPropertyName())) {
294 			// attribute removed from the manager - remove stored value
295 			removePropertyValue(((Attribute)evt.getOldValue()).getName());
296 		}
297 	}
298 
299 	/**
300 	 * Registers this model elements to notifications from its attribute manager about changed properties.
301 	 */
302 	protected final void bindToAttributeManager() {
303 		getAttributeManager().addPropertyChangeListener(this);
304 	}
305 
306 	/**
307 	 * Copies values of all {@link Attribute}s of <code>mappable</code> that are registered in this {@link ModelElement}
308 	 * 's {@link AttributeManager}.
309 	 *
310 	 * If defined, the {@link Attribute#getDefaultMergePolicy()} policies are used in order to update particular
311 	 * attribute value.
312 	 *
313 	 * @param mappable
314 	 *            {@link Mappable} to copy attribute values' from
315 	 */
316 	public void copyAttributeValues(Mappable mappable) {
317 
318 		for (String attributeName : mappable.getAttributeNames()) {
319 
320 			// copy only values of registered attributes
321 			if (getAttributeManager().isRegisteredId(attributeName) && isAttributeSettable(attributeName)) {
322 
323 				AttributeValue attributeValue = getAttributeValue(attributeName);
324 				Object newValue = mappable.getAttribute(attributeName);
325 
326 				// if there is already a value then merge if a policy is set
327 				if (attributeValue != null) {
328 					Attribute attribute = attributeValue.getAttribute();
329 					if (attribute.getDefaultMergePolicy() != null) {
330 						Object oldValue = attributeValue == null ? null : attributeValue.getValue();
331 						newValue = attribute.getDefaultMergePolicy().mergeValues(attribute, oldValue, newValue);
332 					}
333 				}
334 
335 				// modify the value
336 				setAttributeValue(attributeName, newValue);
337 			}
338 
339 		}
340 
341 	}
342 
343 	// END Attribute handling
344 
345 }