/* $Id: FigLifeLine.java 19174 2011-04-02 09:18:46Z linus $ ******************************************************************************* * Copyright (c) 2010 Contributors - see below * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Bob Tarling * Christian L\u00f3pez Esp\u00ednola ******************************************************************************* * * Some portions of this file were previously release using the BSD License: */ // $Id: FigLifeLine.java 19174 2011-04-02 09:18:46Z linus $ // Copyright (c) 2007-2009 The Regents of the University of California. All // Rights Reserved. Permission to use, copy, modify, and distribute this // software and its documentation without fee, and without a written // agreement is hereby granted, provided that the above copyright notice // and this paragraph appear in all copies. This software program and // documentation are copyrighted by The Regents of the University of // California. The software program and documentation are supplied "AS // IS", without any accompanying services from The Regents. The Regents // does not warrant that the operation of the program will be // uninterrupted or error-free. The end-user understands that the program // was developed for research purposes and is advised not to rely // exclusively on the program for any reason. IN NO EVENT SHALL THE // UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, // SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, // ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF // THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF // SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE // PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF // CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, // UPDATES, ENHANCEMENTS, OR MODIFICATIONS. package org.argouml.sequence2.diagram; import java.awt.Rectangle; import java.util.Collections; import java.util.LinkedList; import java.util.List; import org.argouml.uml.diagram.DiagramSettings; import org.argouml.uml.diagram.ui.ArgoFigGroup; import org.tigris.gef.presentation.FigLine; import org.tigris.gef.presentation.FigRect; /** * This fig is the LifeLine of a ClassifierRole. * @author penyaskito */ class FigLifeLine extends ArgoFigGroup { private static final long serialVersionUID = 466925040550356L; private FigLine lineFig; private FigRect rectFig; private List<FigActivation> activations; private List<FigActivation> stackedActivations; static final int WIDTH = 150; static final int HEIGHT = 500; FigLifeLine(Object owner, Rectangle bounds, DiagramSettings settings) { super(owner, settings); initialize(bounds.x, bounds.y); } private void initialize(int x, int y) { activations = new LinkedList<FigActivation>(); stackedActivations = new LinkedList<FigActivation>(); rectFig = new FigRect(x, y, WIDTH, HEIGHT); rectFig.setFilled(false); rectFig.setLineWidth(0); lineFig = new FigLine(x + WIDTH / 2, y, x + WIDTH / 2, y + HEIGHT, LINE_COLOR); lineFig.setDashed(true); lineFig.setLineWidth(LINE_WIDTH); addFig(rectFig); addFig(lineFig); } // TODO: Does this still need to be synchronized? If so then explain why. synchronized void createActivations(final List<FigMessage> messages) { clearActivations(); Collections.sort(messages, new FigMessageComparator()); activations = createStandardActivations(messages); stackedActivations = createStackedActivations(messages); addActivations(activations); addActivations(stackedActivations); // TODO: Do we need this? calcBounds(); } /** * Add the given list of activation Figs to the lifeline. The fill colour * is forced to the lifeline colour in the process. * @param activationFigs */ private void addActivations( final List<FigActivation> activationFigs) { for (final FigActivation figAct : activationFigs) { figAct.setFillColor(getFillColor()); addFig(figAct); } } private List<FigActivation> createStandardActivations( final List<FigMessage> figMessages) { final List<FigActivation> newActivations = new LinkedList<FigActivation>(); // Check here if there are no incoming call actions // if not then create an activation at the top of the lifeline FigActivation currentActivation = null; if (!hasIncomingCallActionFirst(figMessages)) { currentActivation = createActivationFig( getOwner(), lineFig.getX(), lineFig.getY(), lineFig.getWidth(), lineFig.getHeight(), getSettings(), null); } // This counts the number of repeated call/returns that take place // after the first activation. This shouldn't be required once // we handle stacked activations better and once issue 5692 and 5693 // are sorted. int activationsCount = 0; // for (FigMessage figMessage : figMessages) { int ySender = 0; if (!figMessage.isSelfMessage()) { if (isIncoming(figMessage)) { if (currentActivation == null) { if (figMessage.isCallAction()) { // if we are the dest and is a call action, create the // activation, but don't add it until the height is set. ySender = figMessage.getFinalY(); currentActivation = createActivationFig( getOwner(), lineFig.getX(), ySender, 0, 0, getSettings(), figMessage); activationsCount++; } else if (figMessage.isCreateAction()) { // if we are the destination of a create action, // create the entire activation currentActivation = createActivationFig( getOwner(), lineFig.getX(), lineFig.getY(), 0, 0, getSettings(), figMessage); activationsCount++; } } else { if (figMessage.isCallAction() && isSameClassifierRoles( currentActivation.getActivatingMessage(), figMessage)) { activationsCount++; } else if (figMessage.isDestroyAction()) { // if we are the target of a destroy action // the figlifeline ends here and we add the activation ySender = figMessage.getFinalY(); currentActivation.setHeight( ySender - currentActivation.getY()); currentActivation.setDestroy(true); lineFig.setHeight(ySender - getY()); newActivations.add(currentActivation); currentActivation = null; } } } if (isOutgoing(figMessage) && currentActivation != null && currentActivation.isActivatorEnd(figMessage) && --activationsCount == 0) { // if we are the source of a return action // the activation ends here. ySender = figMessage.getStartY(); currentActivation.setHeight( ySender - currentActivation.getY()); newActivations.add(currentActivation); currentActivation = null; } } } // If we have a currentAct object that means have reached the end // of the lifeline with a call or a create not returned. // Add the activation to the list after setting its height to end // at the end of the lifeline. if (currentActivation != null) { currentActivation.setHeight( getHeight() - (currentActivation.getY() - getY())); newActivations.add(currentActivation); } return newActivations; } private boolean isSameClassifierRoles( final FigMessage mess1, final FigMessage mess2) { return mess1 != null && mess1.getDestFigNode() == mess2.getDestFigNode() && mess1.getSourceFigNode() == mess2.getSourceFigNode(); } /** * Return true if the given message fig is pointing in to this lifeline. * @param messageFig * @return true if the message is incoming */ private boolean isIncoming(FigMessage messageFig) { return (messageFig.getDestFigNode().getOwner() == getOwner()); } /** * Return true if the given message fig is pointing out from this lifeline. * @param messageFig * @return true if the message is outgoing */ private boolean isOutgoing(FigMessage messageFig) { return (messageFig.getSourceFigNode().getOwner() == getOwner()); } private FigActivation createActivationFig( final Object owner, final int x, final int y, final int w, final int h, final DiagramSettings settings, final FigMessage messageFig) { return new FigActivation( owner, new Rectangle(x, y, w, h), settings, messageFig); } private List<FigActivation> createStackedActivations( final List<FigMessage> figMessages) { final List<FigActivation> newActivations = new LinkedList<FigActivation>(); FigActivation currentAct = null; for (FigMessage figMessage : figMessages) { int ySender = 0; // if we are the dest and is a call action, create the // activation, but don't add it until the height is set. if (figMessage.isSelfMessage()) { if (figMessage.isCallAction()) { ySender = figMessage.getFinalY(); currentAct = new FigActivation(figMessage.getOwner(), new Rectangle(lineFig.getX() + FigActivation.DEFAULT_WIDTH / 2, ySender, 0, 0), getSettings(), figMessage, false); } else if (currentAct != null && figMessage.isReturnAction()) { ySender = figMessage.getStartY(); currentAct.setHeight(ySender - currentAct.getY()); newActivations.add(currentAct); currentAct = null; } } } return newActivations; } private boolean hasIncomingCallActionFirst( final List<FigMessage> figMessages) { final FigClassifierRole cr = (FigClassifierRole) getGroup(); if (figMessages.isEmpty()) { return false; } FigMessage figMessage = figMessages.get(0); if (cr.equals(figMessage.getDestFigNode()) && !cr.equals(figMessage.getSourceFigNode()) && figMessage.isCallAction()) { return true; } return false; } private void clearActivations() { for (FigActivation oldActivation : activations) { removeFig(oldActivation); } for (FigActivation oldActivation : stackedActivations) { removeFig(oldActivation); } activations.clear(); stackedActivations.clear(); } @Override public void setFilled(boolean filled) { // we do nothing. No call to the parent } @Override protected synchronized void setBoundsImpl(int x, int y, int w, int h) { final Rectangle oldBounds = getBounds(); rectFig.setBounds(x, y, w, h); lineFig.setBounds(x + w / 2, y, w, h); final int yDiff = oldBounds.y - y; // we don't recalculate activations, just move them for (FigActivation act : activations) { // TODO: why do we need to remove then add the Fig? removeFig(act); act.setLocation( lineFig.getX() - FigActivation.DEFAULT_WIDTH / 2, act.getY() - yDiff); if (activations.size() == 1 && act.getHeight() == oldBounds.height) { act.setHeight(getHeight()); } addFig(act); } damage(); _x = x; _y = y; _w = w; _h = h; firePropChange("bounds", oldBounds, getBounds()); } public void setLineWidth(int w) { lineFig.setLineWidth(w); } }