/* * Copyright (c) 2002 No Magic, Inc. All Rights Reserved. */ package com.nomagic.magicdraw.examples.statistics; import com.nomagic.actions.AMConfigurator; import com.nomagic.actions.ActionsManager; import com.nomagic.magicdraw.actions.ActionsConfiguratorsManager; import com.nomagic.magicdraw.actions.BrowserContextAMConfigurator; import com.nomagic.magicdraw.actions.MDActionsCategory; import com.nomagic.magicdraw.plugins.Plugin; import com.nomagic.magicdraw.ui.browser.Node; import com.nomagic.magicdraw.ui.browser.Tree; import com.nomagic.magicdraw.ui.browser.actions.DefaultBrowserAction; import com.nomagic.magicdraw.uml.BaseElement; import com.nomagic.magicdraw.uml.ClassTypes; import com.nomagic.uml2.ext.jmi.reflect.VisitorContext; import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Element; import com.nomagic.uml2.impl.ModelHierarchyVisitor; import javax.annotation.CheckForNull; import javax.swing.*; import java.awt.event.ActionEvent; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Map; /** * Example-Plugin for counting model elements and doing some kind of analysis or statistics. * Demonstrates how to collect children (contained elements) of some element, * how to go through hierarchy and do some element-type specific actions by using InheritanceVisitor. * * @author Nerijus Jankevicius */ public class Statistics extends Plugin { /** * Action, that do all the work. */ private ShowStatistics statsAction; /** * Hierarchy visitor, for counting elements. * Can do different actions with different element types. */ private StatisticsVisitor visitor = new StatisticsVisitor(); /** * Initializing the plugin. * Create and register action. */ @Override public void init() { // create browser action. statsAction = new ShowStatistics(); // register this action in containment tree browser. ActionsConfiguratorsManager.getInstance() .addContainmentBrowserContextConfigurator(new BrowserContextAMConfigurator() { @Override public void configure(ActionsManager manager, Tree browser) { if (statsAction.canBeUsed(browser)) { MDActionsCategory category = new MDActionsCategory(); category.addAction(statsAction); manager.addCategory(category); } } @Override public int getPriority() { return AMConfigurator.MEDIUM_PRIORITY; } }); } /** * Return true always, because this plugin does not have any close specific actions. */ @Override public boolean close() { return true; } /** * @see com.nomagic.magicdraw.plugins.Plugin#isSupported() */ @Override public boolean isSupported() { return true; } /** * The action responsible for starting new counter and displaying results. * Can be invoked on every model element node in the browser tree. */ class ShowStatistics extends DefaultBrowserAction { public ShowStatistics() { super("", "Show Statistics", null, null); } /** * Performs new count. * Clears results map, starts new counter and displays results. */ @Override public void actionPerformed(ActionEvent evt) { visitor.getMap().clear(); // count children of selected in browser element. Tree tree = getTree(); if (tree != null) { final Element element = getSelectedElement(tree); if (element != null) { visitChildren(element); } showResults(visitor.getMap()); } } /** * Displays counting results - data from counter's elements map. * Simply shows Swing message with counting results. */ public void showResults(Map map) { // this text will be displayed in message window. StringBuilder text = new StringBuilder(); // constructs text in style : // Type1 : elements number // Type2 : elements number // ... for (String type : map.keySet()) { text.append(type).append(" : ").append(map.get(type)).append("\n"); } // do some analysis - average number of attributes and operation per class. Integer classes = map.get("ModelClass"); Integer attributes = map.get("Attribute"); Integer operations = map.get("Operation"); if (classes!=null && operations!=null) { text.append("Average operations per class : ").append(operations.doubleValue() / classes.doubleValue()).append("\n"); } if (classes!=null && attributes!=null) { text.append("Average attributes per class : ").append(attributes.doubleValue() / classes.doubleValue()).append("\n"); } // if map was empty and no elements was found. if (text.length()==0) { text = new StringBuilder("No elements found!"); } // displays constructed text. JOptionPane.showMessageDialog(null, text.toString(), "Statistics", JOptionPane.PLAIN_MESSAGE); } /** * Action can be used only if there are selected model element in the browser. */ public boolean canBeUsed(Tree tree) { return getSelectedElement(tree)!=null; } /** * Update menu item. */ public void updateState(Tree tree) { setEnabled(canBeUsed(tree)); } /** * @return selected in given tree model element or null otherwise. */ @CheckForNull public Element getSelectedElement(Tree tree) { if( tree.getSelectedNodes()==null) { return null; } // iterate selected nodes. // checks type of the node, because can be selected and code engineering sets. return (Element) Arrays.stream(tree.getSelectedNodes()) .map(Node::getUserObject) .filter(userObject -> userObject instanceof Element) .findFirst() .orElse(null); // if there is no selected model element. } /** * Goes through all children of given model elements. * Demonstrates way how to collect all children by using FOR cycle and avoiding recursion. * Visit every child with StatisticsVisitor. * * @param root the root model element. */ public void visitChildren(Element root) { ArrayList all = new ArrayList<>(); all.add(root); // if current element has children, list will be increased. for (int i = 0; i < all.size(); i++) { Element current = all.get(i); try { // let's perform some action with this element in visitor. current.accept(visitor); } catch (Exception e) { e.printStackTrace(); } // add all children into end of this list, so it emulates recursion. all.addAll(current.getOwnedElement()); } } } /** * Visitor for counting model elements. * Contains Map for counting results. * Must be overridden visit... method for every counted type of element. * In this example only ModelClass, Attribute and Operation will be counted. */ static class StatisticsVisitor extends ModelHierarchyVisitor { /** * Map for counting results. * Class type is a key, number is a value. */ private HashMap statsTable = new HashMap<>(); /** * Returns map of results. Used to access results from outside. * * @return results map. */ public Map getMap() { return statsTable; } @Override public void visitElement(com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Element element, VisitorContext context) { super.visitElement(element, context); count(element); } /** * Increases value of this type count in results map. * If there is no such registered type, add it. * * @param element the element to count. */ public void count(BaseElement element) { String classType = ClassTypes.getShortName(element.getClassType()); Integer count = statsTable.get(classType); if (count==null) { count = 0; } statsTable.put(classType, count + 1); } } }