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: Diagram.java
13   * Created: 2007-00-00
14   * Author: fox, tmilos
15   * $Id: Diagram.java 2770 2009-04-21 19:17:34Z 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.util.Collection;
23  import java.util.Collections;
24  import java.util.Date;
25  import java.util.HashSet;
26  import java.util.LinkedList;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Set;
30  
31  import org.apache.log4j.Logger;
32  import org.eclipse.core.resources.IFile;
33  import org.eclipse.core.runtime.CoreException;
34  import org.eclipse.core.runtime.IProgressMonitor;
35  import org.eclipse.core.runtime.NullProgressMonitor;
36  import org.eclipse.osgi.util.NLS;
37  
38  import pl.edu.agh.cast.Activator;
39  import pl.edu.agh.cast.model.DefaultStatisticsProvider;
40  import pl.edu.agh.cast.model.attributes.Attribute;
41  import pl.edu.agh.cast.model.attributes.AttributeManager;
42  import pl.edu.agh.cast.model.attributes.AttributeMergePolicy;
43  import pl.edu.agh.cast.model.attributes.AttributeValue;
44  import pl.edu.agh.cast.model.attributes.ConnectionGroupAttributeManager;
45  import pl.edu.agh.cast.model.attributes.DiagramAttributeManager;
46  import pl.edu.agh.cast.model.attributes.NodeAttributeManager;
47  import pl.edu.agh.cast.model.attributes.ValueType;
48  import pl.edu.agh.cast.model.base.DataSet;
49  import pl.edu.agh.cast.model.base.IDataSet;
50  import pl.edu.agh.cast.model.base.IEntity;
51  import pl.edu.agh.cast.model.base.IRelation;
52  import pl.edu.agh.cast.util.Messages;
53  
54  import com.thoughtworks.xstream.annotations.XStreamAlias;
55  
56  /**
57   * Diagram, which consists of of {@link Node}s and {@link Connection}s.
58   *
59   * @author AGH CAST Team
60   */
61  @XStreamAlias("diagram")
62  public class Diagram extends ModelElement implements IDiagram, PropertyChangeListener {
63  
64  	private static final long serialVersionUID = 7475870569424703697L;
65  
66  	/**
67  	 * Default, dummy id of diagrams editor.
68  	 */
69  	public static final String DUMMY_ID = "dummyEditorId"; //$NON-NLS-1$
70  
71  	/**
72  	 * Name of the <em>Diagram name changed</em> event.
73  	 */
74  	public static final String NAME = "Diagram.NAME"; //$NON-NLS-1$
75  
76  	/**
77  	 * Name of the <em>Diagram file changed</em> event.
78  	 */
79  	public static final String FILE = "Diagram.FILE"; //$NON-NLS-1$
80  
81  	/**
82  	 * Name of the <em>Diagram node added/removed</em> event.
83  	 */
84  	public static final String CHILD = "Diagram.CHILD"; //$NON-NLS-1$
85  
86  	/**
87  	 * Name of the <em>Diagram connections changed</em> event.
88  	 */
89  	public static final String CONNECTION = "Diagram.CONNECTION"; //$NON-NLS-1$
90  
91  	/**
92  	 * Name of the <em>Diagram nodes (multiple) added/removed</em> event.
93  	 */
94  	public static final String CHILDREN = "Diagram.CHILDREN"; //$NON-NLS-1$
95  
96  	/**
97  	 * Name of the <em>Diagram nodes deselected</em> event.
98  	 */
99  	public static final String DESELECT = "Diagram.DESELECT"; //$NON-NLS-1$
100 
101 	/**
102 	 * Logger for this class.
103 	 */
104 	protected static Logger log = Activator.getLogger();
105 
106 	/**
107 	 * File this diagram is stored in.
108 	 */
109 	protected transient IFile file;
110 
111 	private boolean enhancable = false;
112 
113 	/**
114 	 * Diagram settings.
115 	 */
116 	@XStreamAlias("settings")
117 	protected IDiagramSettings settings;
118 
119 	/**
120 	 * Node attribute manager.
121 	 */
122 	@XStreamAlias("nodeAttributes")
123 	protected NodeAttributeManager nodeAttributeManager;
124 
125 	/**
126 	 * Connection group attribute manager.
127 	 */
128 	@XStreamAlias("connectionGroupAttributes")
129 	protected ConnectionGroupAttributeManager connectionGroupAttributeManager;
130 
131 	/**
132 	 * Diagram attribute manager.
133 	 */
134 	@XStreamAlias("diagramAttributes")
135 	protected DiagramAttributeManager diagramAttributeManager;
136 
137 	/**
138 	 * Caching factory of visual elements.
139 	 */
140 	@XStreamAlias("connections")
141 	protected final VisualModelCachingFactory visualModelFactory;
142 
143 	/**
144 	 * Whether firing events should be suppressed.
145 	 */
146 	private boolean suppressEvents;
147 
148 	/**
149 	 * Constructs new diagram from model.
150 	 *
151 	 * @param model
152 	 *            base model
153 	 */
154 	public Diagram(Collection<IDataSet> model) {
155 		this(model, new NullProgressMonitor());
156 	}
157 
158 	/**
159 	 * Constructs new diagram from model. Reports progress using a progress monitor.
160 	 *
161 	 * @param model
162 	 *            base model
163 	 * @param monitor
164 	 *            non-null progress monitor
165 	 * @throws IllegalArgumentException
166 	 *             if progress monitor is null
167 	 */
168 	public Diagram(Collection<IDataSet> model, IProgressMonitor monitor) {
169 		this(model, monitor, false);
170 	}
171 
172 	/**
173 	 * Constructs new diagram from model. Reports progress using a progress monitor.
174 	 *
175 	 * @param model
176 	 *            base model
177 	 * @param monitor
178 	 *            non-null progress monitor
179 	 * @param enhancable
180 	 *            whether this Diagram may be enhanced (see {@link IDiagram#isEnhancable()})
181 	 * @throws IllegalArgumentException
182 	 *             if progress monitor is null
183 	 */
184 	public Diagram(Collection<IDataSet> model, IProgressMonitor monitor, boolean enhancable) {
185 		super();
186 
187 		this.enhancable = enhancable;
188 		settings = new DiagramSettings();
189 		settings.addPropertyChangeListener(this);
190 		settings.setEditorId(DUMMY_ID);
191 
192 		nodeAttributeManager = new NodeAttributeManager(model);
193 		connectionGroupAttributeManager = new ConnectionGroupAttributeManager();
194 		diagramAttributeManager = new DiagramAttributeManager();
195 		visualModelFactory = new VisualModelCachingFactory(nodeAttributeManager, connectionGroupAttributeManager);
196 
197 		bindToAttributeManager();
198 
199 		monitor.beginTask(Messages.Diagram_0, 2 * countConnections(model));
200 		createConnections(model, monitor);
201 		createConnectionGroups(monitor);
202 	}
203 
204 	private int countConnections(Collection<IDataSet> model) {
205 		int ret = 0;
206 		for (IDataSet dataSet : model) {
207 			ret += dataSet.getRelations().size();
208 		}
209 		return ret;
210 	}
211 
212 	@Override
213 	protected Object readResolve() {
214 		super.readResolve();
215 		if (nodeAttributeManager == null) {
216 			nodeAttributeManager = new NodeAttributeManager();
217 		}
218 		if (connectionGroupAttributeManager == null) {
219 			connectionGroupAttributeManager = new ConnectionGroupAttributeManager();
220 		}
221 		if (diagramAttributeManager == null) {
222 			diagramAttributeManager = new DiagramAttributeManager();
223 		}
224 		settings.addPropertyChangeListener(this);
225 		visualModelFactory.setupConnectionGroups();
226 		return this;
227 	}
228 
229 	/**
230 	 * {@inheritDoc}
231 	 *
232 	 * @see java.lang.Object#toString()
233 	 */
234 	@Override
235 	public String toString() {
236 		return settings.getDisplayName();
237 	}
238 
239 	/**
240 	 * {@inheritDoc}
241 	 *
242 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#getFile()
243 	 */
244 	public IFile getFile() {
245 		return file;
246 	}
247 
248 	/**
249 	 * {@inheritDoc}
250 	 *
251 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#setFile(org.eclipse.core.resources.IFile)
252 	 */
253 	public void setFile(IFile file) {
254 		IFile oldFile = this.file;
255 		this.file = file;
256 		firePropertyChange(FILE, oldFile, file);
257 	}
258 
259 	/**
260 	 * {@inheritDoc}
261 	 *
262 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#setDisplayName(java.lang.String)
263 	 */
264 	public void setDisplayName(String displayName) {
265 		String oldDispName = settings.getDisplayName();
266 		settings.setDisplayName(displayName);
267 		firePropertyChange(NAME, oldDispName, displayName);
268 		try {
269 			if (file != null) {
270 				file.touch(null);
271 			}
272 		} catch (CoreException e) {
273 			Activator.getLogger().error("error touching file", e); //$NON-NLS-1$
274 		}
275 	}
276 
277 	/**
278 	 * {@inheritDoc}
279 	 *
280 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#getDisplayName()
281 	 */
282 	public String getDisplayName() {
283 		return settings.getDisplayName();
284 	}
285 
286 	/**
287 	 * {@inheritDoc}
288 	 *
289 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#getStatistics()
290 	 */
291 	public List<Statistic> getStatistics() {
292 		return getStatistics(new NullProgressMonitor());
293 	}
294 
295 	/**
296 	 * {@inheritDoc}
297 	 *
298 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#getStatistics(org.eclipse.core.runtime.IProgressMonitor)
299 	 */
300 	public List<Statistic> getStatistics(IProgressMonitor monitor) {
301 		return new DefaultStatisticsProvider().statistics(this, monitor);
302 	}
303 
304 	/**
305 	 * {@inheritDoc}
306 	 *
307 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#getSettings()
308 	 */
309 	public IDiagramSettings getSettings() {
310 		return settings;
311 	}
312 
313 	/**
314 	 * {@inheritDoc}
315 	 *
316 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#setSettings(pl.edu.agh.cast.model.visual.backward.IDiagramSettings)
317 	 */
318 	public void setSettings(IDiagramSettings settings) {
319 		this.settings = settings;
320 	}
321 
322 	/**
323 	 * Populates the set of connections based on the provided relations in base model.
324 	 *
325 	 * @param model
326 	 *            model to populate with
327 	 * @param monitor
328 	 *            non-null progress monitor
329 	 */
330 	private void createConnections(Collection<IDataSet> model, IProgressMonitor monitor) {
331 		monitor.subTask(Messages.Diagram_1);
332 		for (IDataSet dataset : model) {
333 			Set<String> currentDS = new HashSet<String>();
334 			for (IRelation relation : dataset.getRelations()) {
335 				// create connection and any required nodes
336 				visualModelFactory.createConnection(relation, currentDS);
337 				monitor.worked(1);
338 			}
339 		}
340 	}
341 
342 	/**
343 	 * Creates connections for given connection groups.
344 	 *
345 	 * @param monitor
346 	 *            non-null progress monitor
347 	 */
348 	private void createConnectionGroups(IProgressMonitor monitor) {
349 		monitor.subTask(Messages.Diagram_2);
350 		for (Connection connection : visualModelFactory.getConnections()) {
351 			ConnectionGroup cg = visualModelFactory.createConnectionGroup(connection);
352 			cg.addConnection(connection);
353 			monitor.worked(1);
354 		}
355 	}
356 
357 	/**
358 	 * {@inheritDoc}
359 	 *
360 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#addNode(pl.edu.agh.cast.model.visual.backward.Node)
361 	 */
362 	public Node addNode(Node node) {
363 		Node addedNode = addNodeInternal(node);
364 		addConnectionsForNode(node);
365 		firePropertyChange(CHILD, null, addedNode);
366 		return addedNode;
367 	}
368 
369 	/**
370 	 * {@inheritDoc}
371 	 *
372 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#addNodes(java.util.Collection)
373 	 */
374 	public Collection<Node> addNodes(Collection<Node> nodes) {
375 		Collection<Node> addedNodes = new LinkedList<Node>();
376 		for (Node node : nodes) {
377 			Node addedNode = addNodeInternal(node);
378 			// addedNode must be equal to node, but if addedNode is another
379 			// object, that means it was actually added as a new node to the
380 			// diagram
381 			if (addedNode != node) {
382 				addedNodes.add(addedNode);
383 			}
384 		}
385 		// add connections from all provided nodes
386 		for (Node node : nodes) {
387 			addConnectionsForNode(node);
388 		}
389 		firePropertyChange(CHILDREN, null, addedNodes);
390 		return addedNodes;
391 	}
392 
393 	/**
394 	 * Adds a node to the model.
395 	 *
396 	 * @param node
397 	 *            node to add
398 	 */
399 	protected Node addNodeInternal(Node node) {
400 		// ignore nodes that are already present in the diagram (i.e. were
401 		// created by diagram's model element factory)
402 		if (!visualModelFactory.wasCreatedHere(node)) {
403 			return visualModelFactory.copyNode(node);
404 		} else {
405 			return node;
406 		}
407 	}
408 
409 	/**
410 	 * Adds connections of a given node to the diagram.
411 	 *
412 	 * @param node
413 	 *            the node
414 	 */
415 	protected void addConnectionsForNode(Node node) {
416 		for (ConnectionGroup cg : node.getConnectionGroups()) {
417 			boolean first = true;
418 			for (Connection c : cg.getAllConnections()) {
419 				// ignore existing connections
420 				if (visualModelFactory.wasCreatedHere(c)) {
421 					continue;
422 				}
423 
424 				// ignore connections between nodes that don't exist in
425 				// current diagram
426 				if (!visualModelFactory.wasCreatedHere(c.getSourceNode())
427 				        || !visualModelFactory.wasCreatedHere(c.getTargetNode())) {
428 					continue;
429 				}
430 
431 				Connection newConn = visualModelFactory.copyConnection(c);
432 				if (first) {
433 					ConnectionGroup newGroup = visualModelFactory.copyConnectionGroup(cg, newConn);
434 					newGroup.addConnection(newConn);
435 					first = false;
436 				} else {
437 					visualModelFactory.createConnectionGroup(newConn).addConnection(newConn);
438 				}
439 			}
440 		}
441 	}
442 
443 	/**
444 	 * {@inheritDoc}
445 	 *
446 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#addConnection(pl.edu.agh.cast.model.visual.backward.Connection)
447 	 */
448 	public Connection addConnection(Connection c) {
449 		// ignore connections that are already present in the diagram
450 		// (i.e. were created by diagram's model element factory)
451 		Connection connection = c;
452 		if (!visualModelFactory.wasCreatedHere(c)) {
453 			connection = visualModelFactory.copyConnection(c);
454 		}
455 		ConnectionGroup cg = visualModelFactory.createConnectionGroup(connection);
456 		cg.addConnection(connection);
457 
458 		firePropertyChange(CONNECTION, null, connection);
459 		return connection;
460 
461 	}
462 
463 	/**
464 	 * {@inheritDoc}
465 	 *
466 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#addConnections(java.util.Collection)
467 	 */
468 	public void addConnections(Collection<Connection> connections) {
469 		for (Connection c : connections) {
470 			addConnection(c);
471 		}
472 	}
473 
474 	/**
475 	 * {@inheritDoc}
476 	 *
477 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#removeNode(pl.edu.agh.cast.model.visual.backward.Node)
478 	 */
479 	public void removeNode(Node node) {
480 		if (!visualModelFactory.wasCreatedHere(node)) {
481 			throw new IllegalArgumentException(NLS.bind("node {0} is not a child of this model", node)); //$NON-NLS-1$
482 		}
483 		visualModelFactory.removeNode(node);
484 
485 		/*
486 		 * removeConnection can modify connection groups (when the group has no more connections, it is removed). We
487 		 * need to first calculate which connections to remove and then remove them all at once. Otherwise a
488 		 * ConcurrentModificationException is bound to occur.
489 		 */
490 		List<Connection> toRemove = new LinkedList<Connection>();
491 		for (ConnectionGroup cg : node.getConnectionGroups()) {
492 			if (!visualModelFactory.wasCreatedHere(cg)) {
493 				continue;
494 			}
495 
496 			for (Connection c : cg.getAllConnections()) {
497 				if (!visualModelFactory.wasCreatedHere(c)) {
498 					continue;
499 				}
500 
501 				toRemove.add(c);
502 			}
503 		}
504 		for (Connection c : toRemove) {
505 			visualModelFactory.removeConnection(c);
506 		}
507 		firePropertyChange(CHILD, node, null);
508 	}
509 
510 	/**
511 	 * {@inheritDoc}
512 	 *
513 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#removeNodes(java.util.Collection)
514 	 */
515 	public void removeNodes(Collection<Node> nodes) {
516 		for (Node node : nodes) {
517 			removeNode(node);
518 		}
519 	}
520 
521 	/**
522 	 * {@inheritDoc}
523 	 *
524 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#removeConnection(pl.edu.agh.cast.model.visual.backward.Connection)
525 	 */
526 	public void removeConnection(Connection connection) {
527 		if (!visualModelFactory.wasCreatedHere(connection)) {
528 			throw new IllegalArgumentException("connection is not a child of this model"); //$NON-NLS-1$
529 		}
530 		visualModelFactory.removeConnection(connection);
531 		firePropertyChange(CONNECTION, connection, null);
532 	}
533 
534 	/**
535 	 * {@inheritDoc}
536 	 *
537 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#getNodeAttributeManager()
538 	 */
539 	public NodeAttributeManager getNodeAttributeManager() {
540 		return nodeAttributeManager;
541 	}
542 
543 	/**
544 	 * {@inheritDoc}
545 	 *
546 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#getNodes()
547 	 */
548 	public Collection<Node> getNodes() {
549 		return Collections.unmodifiableCollection(visualModelFactory.getNodes());
550 	}
551 
552 	/**
553 	 * {@inheritDoc}
554 	 *
555 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#getConnections()
556 	 */
557 	public Collection<Connection> getConnections() {
558 		return Collections.unmodifiableCollection(visualModelFactory.getConnections());
559 	}
560 
561 	/**
562 	 * Returns caching factory of visual elements.
563 	 *
564 	 * @return caching factory of visual elements
565 	 */
566 	protected final VisualModelCachingFactory getVisualModelFactory() {
567 		return visualModelFactory;
568 	}
569 
570 	/**
571 	 * {@inheritDoc}
572 	 *
573 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#findNode(java.lang.String)
574 	 */
575 	public Node findNode(String nodeId) {
576 		// we could iterate getNodes(), but the factory can
577 		// do it faster
578 		return visualModelFactory.findNode(nodeId);
579 	}
580 
581 	/**
582 	 * {@inheritDoc}
583 	 *
584 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#findConnection(java.lang.String, java.lang.String, java.util.Date)
585 	 */
586 	public Connection findConnection(String sourceNodeId, String targetNodeId, Date date) {
587 		return visualModelFactory.findConnection(sourceNodeId, targetNodeId, date);
588 	}
589 
590 	/**
591 	 * {@inheritDoc}
592 	 *
593 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#addAttributesFromEntities(java.util.Collection, java.util.Collection,
594 	 *      java.util.Map, java.lang.String, org.eclipse.core.runtime.IProgressMonitor)
595 	 */
596 	public void addAttributesFromEntities(Collection<IEntity> entities, Collection<String> attributes,
597 	        Map<String, AttributeMergePolicy> mergePolicies, String nodeType, IProgressMonitor monitor) {
598 		// the key for the ID attribute of IEntity may differ from NodeAttributeManager.PERMANENT_ID
599 		// as it is used via reflection mechanism to access AbstractMappable.getId() method.
600 		String entityIdKey = "Id"; //$NON-NLS-1$
601 		addAttributesFromEntities(entities, attributes, mergePolicies, nodeType, entityIdKey,
602 		        NodeAttributeManager.PERMANENT_ID, monitor);
603 	}
604 
605 	/**
606 	 * {@inheritDoc}
607 	 *
608 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#addAttributesFromEntities(java.util.Collection, java.util.Collection,
609 	 *      java.util.Map, java.lang.String, java.lang.String, java.lang.String,
610 	 *      org.eclipse.core.runtime.IProgressMonitor)
611 	 */
612 	public void addAttributesFromEntities(Collection<IEntity> entities, Collection<String> attributes,
613 	        Map<String, AttributeMergePolicy> mergePolicies, String nodeType, String sourceJoinAttribute,
614 	        String targetJoinAttribute, IProgressMonitor monitor) {
615 
616 		// TODO if nodes have an attribute with the same name as entity, but
617 		// different ValueType then this is not handled correctly
618 
619 		IProgressMonitor monit = monitor;
620 
621 		if (monit == null) {
622 			monit = new NullProgressMonitor();
623 		}
624 		monit.beginTask(NLS.bind(Messages.DiagramEnhancingMonitor_1, getDisplayName()), entities.size()
625 		        + visualModelFactory.getNodeCount());
626 
627 		monit.subTask(Messages.DiagramEnhancingMonitor_2);
628 		NodeIndexer nodeIndexer = new NodeIndexer(visualModelFactory, targetJoinAttribute, nodeType, monit);
629 
630 		monit.subTask(Messages.DiagramEnhancingMonitor_3);
631 
632 		List<String> addedAttributes = new LinkedList<String>();
633 
634 		String[] attributeNames = null;
635 		if (attributes != null) {
636 			attributeNames = attributes.toArray(new String[attributes.size()]);
637 		}
638 
639 		for (IEntity entity : entities) {
640 
641 			// get nodes that should be enhanced
642 			Object joinValue = entity.getAttribute(sourceJoinAttribute);
643 			List<Node> joinedNodes = nodeIndexer.lookup(joinValue);
644 
645 			// if no attribute list was provided or it was empty
646 			// then add all entity attributes
647 			if (attributes == null || attributeNames == null || attributeNames.length == 0) {
648 				attributeNames = entity.getAttributeNames();
649 			}
650 
651 			if (joinedNodes != null && joinedNodes.size() > 0) {
652 				for (Node node : joinedNodes) {
653 					for (String attrName : attributeNames) {
654 
655 						Object entValue = entity.getAttribute(attrName);
656 
657 						// TODO probably should be moved out of the nested loop when
658 						// each type of node will have its own AttributeManager
659 						// check if attribute is new for the node
660 						if (!addedAttributes.contains(attrName)) {
661 							if (!node.getAttributeManager().isRegisteredId(attrName)) {
662 
663 								// remember that this attribute has been added
664 								addedAttributes.add(attrName);
665 
666 								// determine the type of the value
667 								ValueType valType = null;
668 								if (entValue != null) {
669 									valType = ValueType.fromValue(entValue);
670 								}
671 								if (valType == null) {
672 									valType = ValueType.String;
673 								}
674 
675 								// register new attribute
676 								node.getAttributeManager().registerAttribute(attrName, valType);
677 							}
678 						}
679 
680 						// retrieve the attribute instance
681 						Attribute attribute = node.getAttributeManager().getAttribute(attrName);
682 
683 						// display all modified attributes
684 						attribute.setShowAsLabel(attribute.isEditable());
685 
686 						if (attribute.getType() == ValueType.String) {
687 							entValue = entValue == null ? null : entValue.toString();
688 						}
689 
690 						if (addedAttributes.contains(attrName)) {
691 							// if the attribute was newly added then don't bother with policies
692 							node.setAttributeValue(attrName, entValue);
693 						} else if (attribute.isEditable()) {
694 							// if the attribute was already present then use policy to pick new value
695 							AttributeMergePolicy policy = getMergePolicy(attrName, mergePolicies);
696 							AttributeValue nodeValue = node.getAttributeValue(attrName);
697 							Object oldValue = nodeValue == null ? null : nodeValue.getValue();
698 							Object newValue = policy.mergeValues(attribute, oldValue, entValue);
699 							node.setAttributeValue(attrName, newValue);
700 						}
701 
702 					}
703 				}
704 			}
705 			monit.worked(1);
706 		}
707 		monit.done();
708 	}
709 
710 	private AttributeMergePolicy getMergePolicy(String attribute, Map<String, AttributeMergePolicy> mergePolicies) {
711 		if (mergePolicies != null) {
712 			AttributeMergePolicy policy = mergePolicies.get(attribute);
713 			if (policy == null) {
714 				policy = mergePolicies.get(null);
715 			}
716 			if (policy != null) {
717 				return policy;
718 			}
719 		}
720 		return AttributeMergePolicy.MERGE_POLICY_ALWAYS_SECOND;
721 	}
722 
723 	/**
724 	 * {@inheritDoc}
725 	 *
726 	 * @see pl.edu.agh.cast.model.visual.backward.ModelElement#propertyChange(java.beans.PropertyChangeEvent)
727 	 */
728 	@Override
729 	public void propertyChange(PropertyChangeEvent evt) {
730 		if (DiagramSettings.LEGEND_SHOWN.equals(evt.getPropertyName())) {
731 			// forward DiagramSettings events as own
732 			firePropertyChange(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
733 		}
734 	}
735 
736 	/**
737 	 * {@inheritDoc}
738 	 *
739 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#deselect()
740 	 */
741 	public void deselect() {
742 		firePropertyChange(DESELECT, false, true);
743 	}
744 
745 	/**
746 	 * {@inheritDoc}
747 	 *
748 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#getDomainModel()
749 	 */
750 	public IDataSet getDomainModel() {
751 		IDataSet data = new DataSet(getDisplayName());
752 		for (IRelation relation : visualModelFactory.getRelations()) {
753 			data.addRelation(relation);
754 		}
755 		return data;
756 	}
757 
758 	/**
759 	 * {@inheritDoc}
760 	 *
761 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#findRelation(pl.edu.agh.cast.model.visual.backward.Connection)
762 	 */
763 	public IRelation findRelation(Connection conn) {
764 		return visualModelFactory.findRelation(conn);
765 	}
766 
767 	/**
768 	 * {@inheritDoc}
769 	 *
770 	 * @see pl.edu.agh.cast.model.visual.backward.ModelElement#getAttributeManager()
771 	 */
772 	@Override
773 	public AttributeManager getAttributeManager() {
774 		return diagramAttributeManager;
775 	}
776 
777 	/**
778 	 * {@inheritDoc}
779 	 *
780 	 * @see pl.edu.agh.cast.model.visual.backward.IDiagram#isEnhancable()
781 	 */
782 	public boolean isEnhancable() {
783 		return enhancable;
784 	}
785 
786 	/**
787 	 * Suppresses all events fired by this diagram.
788 	 *
789 	 * @param newSuppressEvents
790 	 *            new value of suppression flag
791 	 */
792 	public void setSuppressEvents(boolean newSuppressEvents) {
793 		suppressEvents = newSuppressEvents;
794 		if (!newSuppressEvents) {
795 			// fire dummy events, so that correct refreshing can take place
796 			firePropertyChange(CHILDREN, null, null);
797 			firePropertyChange(CONNECTION, null, null);
798 		}
799 	}
800 
801 	private boolean isSuppressEvents() {
802 		return suppressEvents;
803 	}
804 
805 	/**
806 	 * {@inheritDoc}
807 	 *
808 	 * @see pl.edu.agh.cast.model.visual.backward.ModelElement#firePropertyChange(java.lang.String, java.lang.Object,
809 	 *      java.lang.Object)
810 	 */
811 	@Override
812 	protected void firePropertyChange(String property, Object oldValue, Object newValue) {
813 		if (!isSuppressEvents()) {
814 			super.firePropertyChange(property, oldValue, newValue);
815 		}
816 	}
817 
818 }