alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

// $Id: FigAssociation.java,v 1.70 2004/08/14 15:26:11 mvw Exp $
// Copyright (c) 1996-2004 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.uml.diagram.ui;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.util.Collection;
import java.util.Iterator;
import java.util.Vector;

import org.argouml.application.api.Notation;
import org.argouml.kernel.ProjectManager;
import org.argouml.model.ModelFacade;
import org.argouml.model.uml.UmlFactory;
import org.argouml.model.uml.UmlModelEventPump;
import org.argouml.ui.ArgoJMenu;
import org.tigris.gef.base.Layer;
import org.tigris.gef.base.PathConvPercent;
import org.tigris.gef.base.PathConvPercentPlusConst;
import org.tigris.gef.presentation.ArrowHead;
import org.tigris.gef.presentation.ArrowHeadComposite;
import org.tigris.gef.presentation.ArrowHeadDiamond;
import org.tigris.gef.presentation.ArrowHeadGreater;
import org.tigris.gef.presentation.ArrowHeadNone;
import org.tigris.gef.presentation.FigNode;
import org.tigris.gef.presentation.FigText;

import ru.novosoft.uml.MElementEvent;


/**
 * This class represents an association Fig on a diagram
 *
 */
public class FigAssociation extends FigEdgeModelElement {
   
    // TODO: should be part of some preferences object
    private static final boolean SUPPRESS_BIDIRECTIONAL_ARROWS = true;

    /**
     * Group for the FigTexts concerning the source association end
     */
    private FigTextGroup srcGroup = new FigTextGroup();

    /**
     * Group for the FigTexts concerning the dest association end
     */
    private FigTextGroup destGroup = new FigTextGroup();

    /**
     * Group for the FigTexts concerning the name and stereotype of the 
     * association itself.
     */
    private FigTextGroup middleGroup = new FigTextGroup();
    
    private FigText srcMult, srcRole;
    private FigText destMult, destRole;
    private FigText srcOrdering, destOrdering;

    private ArrowHead sourceArrowHead, destArrowHead;



    /**
     * Main constructor
     * 
     * Don't call this constructor directly. It is public since this
     * is necessary for loading. Use the FigAssociation(Object, Layer)
     * constructor instead!
     */
    public FigAssociation() {
        super();
    
        // lets use groups to construct the different text sections at
        // the association
        middleGroup.addFig(getNameFig());
        middleGroup.addFig(getStereotypeFig());
        addPathItem(middleGroup, new PathConvPercent(this, 50, 25));
    
        srcMult = new FigText(10, 10, 90, 20);
        srcMult.setFont(getLabelFont());
        srcMult.setTextColor(Color.black);
        srcMult.setTextFilled(false);
        srcMult.setFilled(false);
        srcMult.setLineWidth(0);
        srcMult.setMultiLine(false);
        srcMult.setJustification(FigText.JUSTIFY_CENTER);

        srcRole = new FigText(10, 10, 90, 20);
        srcRole.setFont(getLabelFont());
        srcRole.setTextColor(Color.black);
        srcRole.setTextFilled(false);
        srcRole.setFilled(false);
        srcRole.setLineWidth(0);
        srcRole.setMultiLine(false);
        srcRole.setJustification(FigText.JUSTIFY_CENTER);

        srcOrdering = new FigText(10, 10, 90, 20);
        srcOrdering.setFont(getLabelFont());
        srcOrdering.setTextColor(Color.black);
        srcOrdering.setTextFilled(false);
        srcOrdering.setFilled(false);
        srcOrdering.setLineWidth(0);
        srcOrdering.setMultiLine(false);
        srcOrdering.setJustification(FigText.JUSTIFY_CENTER);

        srcGroup.addFig(srcRole);
        srcGroup.addFig(srcOrdering);
        addPathItem(srcMult, new PathConvPercentPlusConst(this, 0, 15, 15));
        addPathItem(srcGroup, new PathConvPercentPlusConst(this, 0, 35, -15));
   
        destMult = new FigText(10, 10, 90, 20);
        destMult.setFont(getLabelFont());
        destMult.setTextColor(Color.black);
        destMult.setTextFilled(false);
        destMult.setFilled(false);
        destMult.setLineWidth(0);
        destMult.setMultiLine(false);
        destMult.setJustification(FigText.JUSTIFY_CENTER);

        destRole = new FigText(0, 0, 90, 20);
        destRole.setFont(getLabelFont());
        destRole.setTextColor(Color.black);
        destRole.setTextFilled(false);
        destRole.setFilled(false);
        destRole.setLineWidth(0);
        destRole.setMultiLine(false);
        destRole.setJustification(FigText.JUSTIFY_CENTER);

        destOrdering = new FigText(0, 0, 90, 20);
        destOrdering.setFont(getLabelFont());
        destOrdering.setTextColor(Color.black);
        destOrdering.setTextFilled(false);
        destOrdering.setFilled(false);
        destOrdering.setLineWidth(0);
        destOrdering.setMultiLine(false);
        destOrdering.setJustification(FigText.JUSTIFY_CENTER);

        destGroup.addFig(destRole);
        destGroup.addFig(destOrdering);
        addPathItem(destMult,
		    new PathConvPercentPlusConst(this, 100, -15, 15));
        addPathItem(destGroup,
		    new PathConvPercentPlusConst(this, 100, -35, -15));
    
        setBetweenNearestPoints(true);
        // next line necessary for loading
        setLayer(ProjectManager.getManager().getCurrentProject()
		 .getActiveDiagram().getLayer());
    }

    /**
     * Constructor that hooks the Fig to an existing UML element
     * @param edge the UMl element
     * @param lay the layer
     */
    public FigAssociation(Object edge, Layer lay) {
	this();
	setLayer(lay);
	setOwner(edge);
    }

    /**
     * @see org.tigris.gef.presentation.Fig#setOwner(java.lang.Object)
     */
    public void setOwner(Object association) {
	super.setOwner(association);

	if (org.argouml.model.ModelFacade.isAAssociation(association)) {
	    Collection connections = ModelFacade.getConnections(association);
	    for (int i = 0; i < connections.size(); i++) {
		Object assEnd = (connections.toArray())[i];
		UmlModelEventPump.getPump()
		    .removeModelEventListener(this, assEnd);
		UmlModelEventPump.getPump()
		    .addModelEventListener(this, assEnd);
	    }
	    UmlModelEventPump.getPump()
		.removeModelEventListener(this, association);
	    UmlModelEventPump.getPump()
		.addModelEventListener(this, association);
	    Object assEnd1 = (connections.toArray())[0];
	    FigNode destNode =
		(FigNode) getLayer()
		    .presentationFor(ModelFacade.getType(assEnd1));
	    FigNode srcNode =
		(FigNode) getLayer()
		    .presentationFor(ModelFacade.getType(assEnd1));
	    if (destNode != null) {
		setDestFigNode(destNode);
		setDestPortFig(destNode);
	    }
	    if (srcNode != null) {
		setSourceFigNode(srcNode); 
		setSourcePortFig(srcNode);  
	    }
	}
   
	modelChanged(null);
    }

    ////////////////////////////////////////////////////////////////
    // event handlers

    /**
     * @see org.argouml.uml.diagram.ui.FigEdgeModelElement#textEdited(org.tigris.gef.presentation.FigText)
     */
    protected void textEdited(FigText ft) {
	
        if (getOwner() == null) return;
	super.textEdited(ft);

	Collection conn = ModelFacade.getConnections(getOwner());
	if (conn == null || conn.size() == 0) return;

	if (ft == srcRole) {
	    Object srcAE = (conn.toArray())[0];
	    ModelFacade.setName(srcAE, srcRole.getText());
	} else if (ft == destRole) {
	    Object destAE = (conn.toArray())[1];
	    ModelFacade.setName(destAE, destRole.getText());
	} else if (ft == srcMult) {
	    Object srcAE = (conn.toArray())[0];
	    Object multi = UmlFactory.getFactory().getDataTypes()
			   .createMultiplicity(srcMult.getText());
	    ModelFacade.setMultiplicity(srcAE, multi);
	} else if (ft == destMult) {
	    Object destAE = (conn.toArray())[1];
	    Object multi = UmlFactory.getFactory().getDataTypes()
			   .createMultiplicity(destMult.getText());
	    ModelFacade.setMultiplicity(destAE, multi);
	}
    }
  
    private void updateEnd(FigText multiToUpdate, FigText roleToUpdate,
			   FigText orderingToUpdate,
			   Object end) {
                               
        if (!ModelFacade.isAAssociationEnd(end)) {
            throw new IllegalArgumentException();
	}
        
	Object multi = ModelFacade.getMultiplicity(end);
	String name = ModelFacade.getName(end);
	Object order = ModelFacade.getOrdering(end);
        String visi = ""; 
        Object stereo = null;
        if (ModelFacade.isNavigable(end)
	    && (ModelFacade.isAClass(ModelFacade.getType(end))
		|| ModelFacade.isAInterface(ModelFacade.getType(end)))) {
	    visi = Notation.generate(this, ModelFacade.getVisibility(end));
        }
        if (ModelFacade.getStereotypes(end).size() > 0) {
            stereo = ModelFacade.getStereotypes(end).iterator().next();
        }
    
	multiToUpdate.setText(Notation.generate(this, multi));
	orderingToUpdate.setText(getOrderingName(order));
	if (stereo != null) {
	    roleToUpdate.setText(Notation.generate(this, stereo)
				 + " " + visi
				 + Notation.generate(this, name));
	} else
	    roleToUpdate.setText(visi + Notation.generate(this, name));
    }

    /**
     * @see org.argouml.uml.diagram.ui.FigEdgeModelElement#modelChanged(ru.novosoft.uml.MElementEvent)
     */
    protected void modelChanged(MElementEvent e) {
	super.modelChanged(e);
	Object associationEnd = getOwner(); //MAssociation
	if (associationEnd == null || getLayer() == null) return;
        
        if (e == null || e.getName().equals("isAbstract")) {
            updateAbstract();
            damage();
        }
    
	//MAssociationEnd
	Object ae0 =
	    ((ModelFacade.getConnections(associationEnd)).toArray())[0];
	//MAssociationEnd
	Object ae1 =
	    ((ModelFacade.getConnections(associationEnd)).toArray())[1];
	updateEnd(srcMult, srcRole, srcOrdering, ae0);
	updateEnd(destMult, destRole, destOrdering, ae1);
    
	boolean srcNav = ModelFacade.isNavigable(ae0);
	boolean destNav = ModelFacade.isNavigable(ae1);
	if (srcNav && destNav && SUPPRESS_BIDIRECTIONAL_ARROWS) {
	    srcNav = false;
	    destNav = false;
	}
	sourceArrowHead =
	    chooseArrowHead(ModelFacade.getAggregation(ae0), srcNav);
	destArrowHead =
	    chooseArrowHead(ModelFacade.getAggregation(ae1), destNav);
	setSourceArrowHead(sourceArrowHead);
	setDestArrowHead(destArrowHead);
	srcGroup.calcBounds();
	destGroup.calcBounds();
	middleGroup.calcBounds();
	this.computeRoute();
    }

    private static final ArrowHead NAV_AGGREGATE =
	new ArrowHeadComposite(ArrowHeadDiamond.WhiteDiamond,
			       new ArrowHeadGreater());

    private static final ArrowHead NAV_COMP =
	new ArrowHeadComposite(ArrowHeadDiamond.BlackDiamond,
			       new ArrowHeadGreater());

    /**
     * @param ak Object of type AggregationKind
     * @param nav the result of a ModelFacade.isNavigable(AssociationEnd)
     * @return the ArrowHead chosen
     */
    private ArrowHead chooseArrowHead(Object ak, boolean nav) {
        
	ArrowHead arrow = ArrowHeadNone.TheInstance;

	if (nav) {
	    if (ModelFacade.NONE_AGGREGATIONKIND.equals(ak) || (ak == null)) {
		arrow = new ArrowHeadGreater();
            } else if (ModelFacade.AGGREGATE_AGGREGATIONKIND.equals(ak)) {
		arrow = NAV_AGGREGATE;
            } else if (ModelFacade.COMPOSITE_AGGREGATIONKIND.equals(ak)) {
		arrow = NAV_COMP;
            }
	} else {
	    if (ModelFacade.NONE_AGGREGATIONKIND.equals(ak) || (ak == null)) {
		arrow = ArrowHeadNone.TheInstance;
	    } else if (ModelFacade.AGGREGATE_AGGREGATIONKIND.equals(ak)) {
		arrow = ArrowHeadDiamond.WhiteDiamond;
	    } else if (ModelFacade.COMPOSITE_AGGREGATIONKIND.equals(ak)) {
		arrow = ArrowHeadDiamond.BlackDiamond;
	    }
	}
	return arrow;
    }

    /**
     * @see org.tigris.gef.ui.PopupGenerator#getPopUpActions(java.awt.event.MouseEvent)
     */
    public Vector getPopUpActions(MouseEvent me) {
	Vector popUpActions = super.getPopUpActions(me);
	// x^2 + y^2 = r^2  (equation of a circle)
	Point firstPoint = this.getFirstPoint();
	Point lastPoint = this.getLastPoint();
	int length = getPerimeterLength();

	int rSquared = (int) (.3 * length);

	// max distance is set at 100 pixels, (rSquared = 100^2)
	if ( rSquared > 100 ) {
	    rSquared = 10000;
        } else {
	    rSquared *= rSquared;
        }
        
	int srcDeterminingFactor =
	    getSquaredDistance(me.getPoint(), firstPoint);
	int destDeterminingFactor =
	    getSquaredDistance(me.getPoint(), lastPoint);

	if (srcDeterminingFactor < rSquared
	    && srcDeterminingFactor < destDeterminingFactor) {

            ArgoJMenu multMenu =
		new ArgoJMenu(BUNDLE, "menu.popup.multiplicity");

            multMenu.add(ActionMultiplicity.getSrcMultOne());
            multMenu.add(ActionMultiplicity.getSrcMultZeroToOne());
            multMenu.add(ActionMultiplicity.getSrcMultOneToMany());
            multMenu.add(ActionMultiplicity.getSrcMultZeroToMany());
            popUpActions.insertElementAt(multMenu, 
                popUpActions.size() - POPUP_ADD_OFFSET);

            ArgoJMenu aggMenu = new ArgoJMenu(BUNDLE, "menu.popup.aggregation");
        
	    aggMenu.add(ActionAggregation.getSrcAggNone());
	    aggMenu.add(ActionAggregation.getSrcAgg());
	    aggMenu.add(ActionAggregation.getSrcAggComposite());
	    popUpActions.insertElementAt(aggMenu, 
					 (popUpActions.size()
					  - POPUP_ADD_OFFSET));
	}
	else if (destDeterminingFactor < rSquared) {
            ArgoJMenu multMenu =
		new ArgoJMenu(BUNDLE, "menu.popup.multiplicity");
	    multMenu.add(ActionMultiplicity.getDestMultOne());
	    multMenu.add(ActionMultiplicity.getDestMultZeroToOne());
	    multMenu.add(ActionMultiplicity.getDestMultOneToMany());
	    multMenu.add(ActionMultiplicity.getDestMultZeroToMany());
	    popUpActions.insertElementAt(multMenu, 
					 (popUpActions.size()
					  - POPUP_ADD_OFFSET));

            ArgoJMenu aggMenu = new ArgoJMenu(BUNDLE, "menu.popup.aggregation");
	    aggMenu.add(ActionAggregation.getDestAggNone());
	    aggMenu.add(ActionAggregation.getDestAgg());
	    aggMenu.add(ActionAggregation.getDestAggComposite());
	    popUpActions.insertElementAt(aggMenu, 
					 (popUpActions.size()
					  - POPUP_ADD_OFFSET));
	}
	// else: No particular options for right click in middle of line

	// Options available when right click anywhere on line
	Object association = getOwner();
	if (association != null) {
	    // Navigability menu with suboptions built dynamically to
	    // allow navigability from atart to end, from end to start
	    // or bidirectional
	    Collection ascEnds = ModelFacade.getConnections(association);
            Iterator iter = ascEnds.iterator();
	    Object ascStart = iter.next();
	    Object ascEnd = iter.next();

	    if (ModelFacade.isAClassifier(ModelFacade.getType(ascStart))
                    && ModelFacade.isAClassifier(ModelFacade.getType(ascEnd))) {
                ArgoJMenu navMenu =
		    new ArgoJMenu(BUNDLE, "menu.popup.navigability");
        
		navMenu.add(ActionNavigability.newActionNavigability(
                    ascStart,
		    ascEnd,
		    ActionNavigability.BIDIRECTIONAL));
		navMenu.add(ActionNavigability.newActionNavigability(
                    ascStart,
		    ascEnd,
		    ActionNavigability.STARTTOEND));
		navMenu.add(ActionNavigability.newActionNavigability(
                    ascStart,
                    ascEnd,
                    ActionNavigability.ENDTOSTART));

		popUpActions.insertElementAt(navMenu, 
					     (popUpActions.size()
					      - POPUP_ADD_OFFSET));
	    }
	}

	return popUpActions;
    }

    /** Returns the name of the OrderingKind.
     * @return "{ordered}", "{sorted}" or "" if null or "unordered"
     */
    private String getOrderingName(Object orderingKind) {
	if (orderingKind == null) return "";
	if (ModelFacade.getName(orderingKind) == null) return ""; 
	if ("".equals(ModelFacade.getName(orderingKind))) return "";
	if ("unordered".equals(ModelFacade.getName(orderingKind))) return "";
        
	return "{" + ModelFacade.getName(orderingKind) + "}";
    }

    /**
     * Updates the name if modelchanged receives an "isAbstract" event
     */
    protected void updateAbstract() {
        Rectangle rect = getBounds();
        if (getOwner() == null)
            return;
        Object assoc =  getOwner();
        if (ModelFacade.isAbstract(assoc))
            getNameFig().setFont(getItalicLabelFont());
        else
            getNameFig().setFont(getLabelFont());
        super.updateNameText();
        setBounds(rect.x, rect.y, rect.width, rect.height);
    }
    
    static final long serialVersionUID = 9100125695919853919L;
  
    /**
     * @see org.tigris.gef.presentation.Fig#paint(java.awt.Graphics)
     */
    public void paint(Graphics g) {
        if (sourceArrowHead == null || destArrowHead == null) {
	    modelChanged(null);
        }
        if (sourceArrowHead != null && destArrowHead != null) {
	    sourceArrowHead.setLineColor(getLineColor());
	    destArrowHead.setLineColor(getLineColor());   
        }
        super.paint(g);
    }

} /* end class FigAssociation */
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.