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: IMoveable.java
13   * Created: 2007-00-00
14   * Author: fox, awos
15   * $Id: Node.java 3054 2009-07-27 10:23:07Z kpietak $
16   */
17  
18  package pl.edu.agh.cast.model.visual.backward;
19  
20  import java.util.Collections;
21  import java.util.LinkedList;
22  import java.util.List;
23  
24  import org.eclipse.draw2d.geometry.Dimension;
25  import org.eclipse.draw2d.geometry.Point;
26  
27  import pl.edu.agh.cast.Activator;
28  import pl.edu.agh.cast.backward.figure.icons.NodeIconProvider;
29  import pl.edu.agh.cast.model.attributes.Attribute;
30  import pl.edu.agh.cast.model.attributes.AttributeManager;
31  import pl.edu.agh.cast.model.attributes.AttributeValue;
32  import pl.edu.agh.cast.model.attributes.NodeAttributeManager;
33  
34  import com.thoughtworks.xstream.annotations.XStreamAlias;
35  import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
36  import com.thoughtworks.xstream.annotations.XStreamOmitField;
37  
38  /**
39   * Class representing nodes of {@link IDiagram}.
40   *
41   * @author AGH CAST Team
42   */
43  @XStreamAlias("node")
44  public class Node extends ModelElement implements Cloneable, IMoveable {
45  
46  	/*
47  	 * XXX: IMPORTANT!! When adding new fields to this class, be careful to update the copyTo method.
48  	 */
49  
50  	private static boolean suppressLocationChangeEvents = false;
51  
52  	private static final long serialVersionUID = -7236469860157722758L;
53  
54  	// BEGIN Events fired by node
55  
56  	/**
57  	 * Name of the <em>Location changed</em> event.
58  	 */
59  	public static final String LOCATION = "Node.location"; //$NON-NLS-1$
60  
61  	/**
62  	 * Name of the <em>Connections changed</em> event.
63  	 */
64  	public static final String CONNECTIONS = "Node.Connections"; //$NON-NLS-1$
65  
66  	/**
67  	 * Name of the <em>Selected</em> event.
68  	 */
69  	public static final String SELECTED = "Node.selected"; //$NON-NLS-1$
70  
71  	/**
72  	 * Name of the <em>Selected primary</em> event.
73  	 */
74  	public static final String SELECTED_PRIMARY = "Node.selectedPrimary"; //$NON-NLS-1$
75  
76  	// END Events fired by node
77  
78  	/**
79  	 * Default node type.
80  	 */
81  	public static final String DEFAULT_NODE_TYPE = "default"; //$NON-NLS-1$
82  
83  	/**
84  	 * Location of the node.
85  	 */
86  	@XStreamAlias("location")
87  	protected Point location = new Point(-1, -1);
88  
89  	/**
90  	 * Dimensions of the node.
91  	 */
92  	@XStreamAlias("dimensions")
93  	protected Dimension dimensions = new Dimension(60, 60);
94  
95  	/**
96  	 * Type of the node.
97  	 */
98  	@XStreamAlias("type")
99  	@XStreamAsAttribute
100 	private String type = DEFAULT_NODE_TYPE;
101 
102 	/**
103 	 * Node's image identifier - one from {@link pl.edu.agh.cast.util.Images}.
104 	 */
105 	@XStreamAlias("imageId")
106 	@XStreamAsAttribute
107 	protected String imageId;
108 
109 	/**
110 	 * List of {@link ConnectionGroup}s adjacent to the node.
111 	 */
112 	@XStreamOmitField
113 	protected List<ConnectionGroup> connectionGroups;
114 
115 	/**
116 	 * Node's {@link AttributeManager}.
117 	 */
118 	@XStreamAlias("attributeManager")
119 	private NodeAttributeManager attributeManager;
120 
121 	/**
122 	 * Creates new Node instance.
123 	 *
124 	 * @param id
125 	 *            id of the node; non-null
126 	 * @param isMainNode
127 	 *            whether the node is a <em>main</em> node or not. Main nodes are highlighted on diagrams.
128 	 * @param attributeManager
129 	 *            {@link AttributeManager} for the node
130 	 * @throws IllegalArgumentException
131 	 *             if supplied node id is null
132 	 */
133 	public Node(String id, boolean isMainNode, NodeAttributeManager attributeManager) {
134 		super();
135 
136 		this.attributeManager = attributeManager;
137 
138 		if (id == null) {
139 			throw new IllegalArgumentException("Node id cannot be null"); //$NON-NLS-1$
140 		}
141 
142 		// default label = id
143 		setAttributeValue(NodeAttributeManager.PERMANENT_ID, id);
144 		setAttributeValue(NodeAttributeManager.PERMANENT_LABEL, id);
145 		setAttributeValue(NodeAttributeManager.PERMANENT_ISMAIN, Boolean.valueOf(isMainNode));
146 		connectionGroups = new LinkedList<ConnectionGroup>();
147 
148 		bindToAttributeManager();
149 	}
150 
151 	/**
152 	 * Creates new Node instance in specified location.
153 	 *
154 	 * @param id
155 	 *            id of the node
156 	 * @param isMainNode
157 	 *            whether the node is a <em>main</em> node or not. Main nodes are highlighted on diagrams.
158 	 * @param attributeManager
159 	 *            {@link AttributeManager} for the node
160 	 * @param location
161 	 *            the location of the node
162 	 */
163 	protected Node(String id, boolean isMainNode, NodeAttributeManager attributeManager, Point location) {
164 		this(id, isMainNode, attributeManager);
165 		this.location = location;
166 	}
167 
168 	/**
169 	 * Inits transient fields during deserialization.
170 	 *
171 	 * {@inheritDoc}
172 	 *
173 	 * @see pl.edu.agh.cast.model.visual.backward.ModelElement#readResolve()
174 	 */
175 	@Override
176 	protected Object readResolve() {
177 		this.connectionGroups = new LinkedList<ConnectionGroup>();
178 		if (this.attributeManager == null) {
179 			Activator.getLogger().debug("Creating new NodeAttributeManager"); //$NON-NLS-1$
180 			this.attributeManager = new NodeAttributeManager();
181 		}
182 		super.readResolve();
183 		return this;
184 	}
185 
186 	/**
187 	 * Adds adjacent {@link ConnectionGroup} to this node.
188 	 *
189 	 * @param connectionGroup
190 	 *            {@link ConnectionGroup} to add
191 	 */
192 	public void addConnectionGroup(ConnectionGroup connectionGroup) {
193 		if (!connectionGroups.contains(connectionGroup)) {
194 			connectionGroups.add(connectionGroup);
195 			firePropertyChange(CONNECTIONS, null, connectionGroup);
196 		}
197 	}
198 
199 	/**
200 	 * Removes adjacent {@link ConnectionGroup} to this node.
201 	 *
202 	 * @param connectionGroup
203 	 *            {@link ConnectionGroup} to remove
204 	 * @return status of the operation (see {@link List#remove(Object)} for details)
205 	 */
206 	public boolean removeConnectionGroup(ConnectionGroup connectionGroup) {
207 		boolean result = connectionGroups.remove(connectionGroup);
208 		firePropertyChange(CONNECTIONS, connectionGroup, null);
209 		return result;
210 	}
211 
212 	/**
213 	 * Returns the list of all {@link ConnectionGroup}s adjacent to this node.
214 	 *
215 	 * @return list of all adjacent {@link ConnectionGroup}s
216 	 */
217 	public List<ConnectionGroup> getConnectionGroups() {
218 		return Collections.unmodifiableList(connectionGroups);
219 	}
220 
221 	/**
222 	 * Returns the Id of this node.
223 	 *
224 	 * @return Id of this node
225 	 */
226 	public String getId() {
227 		AttributeValue attributeValue = getAttributeValue(NodeAttributeManager.PERMANENT_ID);
228 		return (String)attributeValue.getValue();
229 	}
230 
231 	/**
232 	 * Returns a label, composed of values of all {@link pl.edu.agh.cast.model.attributes.ValueType#String} attributes
233 	 * that have {@link Attribute#isShowAsLabel()} set.
234 	 *
235 	 * {@inheritDoc}
236 	 *
237 	 * @see pl.edu.agh.cast.model.visual.backward.IMoveable#getLabel()
238 	 */
239 	public String getLabel() {
240 		StringBuilder part1 = new StringBuilder(60);
241 		StringBuilder part2 = new StringBuilder(60);
242 		// first append all permanent attributes
243 		for (Attribute a : getAttributeManager().getAttributes()) {
244 			if (a.isShowAsLabel()) {
245 				AttributeValue attributeValue = getAttributeValue(a.getName());
246 				if (null != attributeValue.getValue()) {
247 					String s = attributeValue.format();
248 					if (attributeValue.getAttribute().getType().isList()) {
249 						s = s.replace(';', '\n');
250 					}
251 					(a.isPermanent() ? part1 : part2).append(s).append('\n');
252 				}
253 			}
254 		}
255 		if (part1.length() > 0 || part2.length() > 0) {
256 			// remove part1's last "\n" if part2 is empty
257 			if (part1.length() > 0 && part2.length() == 0) {
258 				part1.delete(part1.length() - 1, part1.length());
259 			}
260 			if (part2.length() > 0) {
261 				part2.delete(part2.length() - 1, part2.length());
262 			}
263 			return part1.append(part2).toString();
264 		} else {
265 			return ""; //$NON-NLS-1$
266 		}
267 	}
268 
269 	/**
270 	 * Returns <em>main</em> flag for this node.
271 	 *
272 	 * @return <em>main</em> node flag
273 	 */
274 	public boolean isMainNode() {
275 		return ((Boolean)getAttributeValue(NodeAttributeManager.PERMANENT_ISMAIN).getValue()).booleanValue();
276 	}
277 
278 	/**
279 	 * Sets the type of this node.
280 	 *
281 	 * @param type
282 	 *            type of the node
283 	 */
284 	public void setType(String type) {
285 		this.type = type;
286 	}
287 
288 	/**
289 	 * Returns the type of this node.
290 	 *
291 	 * @return type of this node
292 	 */
293 	public String getType() {
294 		return type;
295 	}
296 
297 	/**
298 	 * {@inheritDoc}
299 	 *
300 	 * @see pl.edu.agh.cast.model.visual.backward.IMoveable#getLocation()
301 	 */
302 	public Point getLocation() {
303 		return location;
304 	}
305 
306 	/**
307 	 * {@inheritDoc}
308 	 *
309 	 * @see pl.edu.agh.cast.model.visual.backward.IMoveable#setLocation(org.eclipse.draw2d.geometry.Point)
310 	 */
311 	public void setLocation(Point newLocation) {
312 		Point oldLocation = location;
313 		location = newLocation;
314 		if (!Node.isSuppressLocationChangeEvents()) {
315 			firePropertyChange(Node.LOCATION, oldLocation, newLocation);
316 		}
317 	}
318 
319 	/**
320 	 * Returns the dimension of this node.
321 	 *
322 	 * @return dimension of this node
323 	 */
324 	public Dimension getDimensions() {
325 		return dimensions;
326 	}
327 
328 	/**
329 	 * Sets dimensions of Node. SHOULD ONLY be called once, after corresponding figure's dimensions are calculated.
330 	 *
331 	 * @param dimensions
332 	 *            new dimensions of this node
333 	 */
334 	public void setDimensions(Dimension dimensions) {
335 		this.dimensions = dimensions;
336 	}
337 
338 	/**
339 	 * Copies all fields of this {@link Node} instance to <code>that</code> instance.
340 	 *
341 	 * @param that
342 	 *            instance of {@link Node} to copy this instance to
343 	 */
344 	public void copyTo(Node that) {
345 		that.attributeManager = attributeManager;
346 		copyAttributes(that);
347 
348 		that.setDimensions(this.getDimensions());
349 		that.setLocation(this.getLocation());
350 		that.setImageId(this.getImageId());
351 		that.setType(this.getType());
352 		// XXX: add code for any new fields in Node here
353 	}
354 
355 	/**
356 	 * Returns the Id of an image that is used to display this node.
357 	 *
358 	 * @return Id of this node's image
359 	 */
360 	public String getImageId() {
361 		if (imageId == null) {
362 			return NodeIconProvider.getInstance().getIconId(getType());
363 		} else {
364 			return imageId;
365 		}
366 	}
367 
368 	public void setImageId(String id) {
369 		imageId = id;
370 	}
371 
372 	/**
373 	 * Returns the number of {@link Connection}s this node is target for.
374 	 *
375 	 * @return number of {@link Connection}s this node is target for
376 	 */
377 	public int getTotalTargetConnectionsCount() {
378 		int count = 0;
379 		for (ConnectionGroup group : connectionGroups) {
380 			count += group.getTargetConnectionCountFor(this);
381 		}
382 		return count;
383 	}
384 
385 	/**
386 	 * Returns the number of {@link Connection}s this node is source for.
387 	 *
388 	 * @return number of {@link Connection}s this node is source for
389 	 */
390 	public int getTotalSourceConnectionsCount() {
391 		int count = 0;
392 		for (ConnectionGroup group : connectionGroups) {
393 			count += group.getSourceConnectionCountFor(this);
394 		}
395 		return count;
396 	}
397 
398 	/**
399 	 * Returns the number of {@link Connection}s this node is source or target for.
400 	 *
401 	 * @return number of {@link Connection}s this node is source or target for
402 	 */
403 	public int getTotalConnectionsCount() {
404 		return getTotalTargetConnectionsCount() + getTotalSourceConnectionsCount();
405 	}
406 
407 	/**
408 	 * Checks if this node is directly connected to given <code>node</code>.
409 	 *
410 	 * @param node
411 	 *            another node to check connection for
412 	 * @return <code>true</code> if there is a direct {@link Connection} between this node and given <code>node</code>
413 	 */
414 	public boolean isConnected(Node node) {
415 		for (ConnectionGroup group : getConnectionGroups()) {
416 			if (group.isBetween(this, node) && (group.getConnectionCount() > 0)) {
417 				return true;
418 			}
419 		}
420 		return false;
421 	}
422 
423 	/**
424 	 * {@inheritDoc}
425 	 *
426 	 * @see java.lang.Object#toString()
427 	 */
428 	@Override
429 	public String toString() {
430 		return "PN " + getLabel(); //$NON-NLS-1$
431 	}
432 
433 	/**
434 	 * Two nodes are equal if their {@link #getId()}s are equal.
435 	 *
436 	 * {@inheritDoc}
437 	 *
438 	 * @see java.lang.Object#equals(java.lang.Object)
439 	 */
440 	@Override
441 	public boolean equals(Object obj) {
442 		if (obj == this) {
443 			return true;
444 		}
445 		if (!(obj instanceof Node)) {
446 			return false;
447 		}
448 
449 		Node that = (Node)obj;
450 		if (!getId().equals(that.getId())) {
451 			return false;
452 		}
453 		return true;
454 	}
455 
456 	/**
457 	 * {@inheritDoc}
458 	 *
459 	 * @see java.lang.Object#hashCode()
460 	 */
461 	@Override
462 	public int hashCode() {
463 		return getId().hashCode();
464 	}
465 
466 	/**
467 	 * Selects this node.
468 	 *
469 	 * @param primary
470 	 *            whether the selection is primary
471 	 */
472 	public void select(boolean primary) {
473 		// UNUSED [tmilos]
474 		if (primary) {
475 			firePropertyChange(SELECTED_PRIMARY, false, true);
476 		} else {
477 			firePropertyChange(SELECTED, false, true);
478 		}
479 	}
480 
481 	/**
482 	 * {@inheritDoc}
483 	 *
484 	 * @see pl.edu.agh.cast.model.visual.backward.ModelElement#getAttributeManager()
485 	 */
486 	@Override
487 	public AttributeManager getAttributeManager() {
488 		return attributeManager;
489 	}
490 
491 	public static void setSuppressLocationChangeEvents(boolean suppressLocationChangeEvents) {
492 		Node.suppressLocationChangeEvents = suppressLocationChangeEvents;
493 	}
494 
495 	public static boolean isSuppressLocationChangeEvents() {
496 		return suppressLocationChangeEvents;
497 	}
498 
499 }