1 package cz
.cvut
.promod
.services
.menuService
;
3 import cz
.cvut
.promod
.gui
.ModelerModel
;
4 import cz
.cvut
.promod
.services
.ModelerSession
;
5 import cz
.cvut
.promod
.services
.actionService
.actionUtils
.ProModAction
;
6 import cz
.cvut
.promod
.services
.menuService
.utils
.*;
7 import org
.apache
.log4j
.Logger
;
11 import java
.util
.ArrayList
;
12 import java
.util
.Arrays
;
13 import java
.util
.LinkedList
;
14 import java
.util
.List
;
17 * ProMod, master thesis project
18 * User: Petr Zverina, petr.zverina@gmail.com
19 * Date: 17:49:26, 10.10.2009
23 * Implementation of MenuService and MenuControlService.
25 public class MenuControlServiceImpl
implements MenuControlService
{
27 private final Logger LOG
= Logger
.getLogger(MenuControlServiceImpl
.class);
29 private final JPopupMenu projectTreePopupMenu
;
30 private final JMenuBar menuBar
;
31 private final MenuValidator menuValidator
;
33 public MenuControlServiceImpl(){
34 projectTreePopupMenu
= ModelerSession
.getComponentFactoryService().createPopupMenu();
35 menuBar
= ModelerSession
.getComponentFactoryService().createMenuBar();
36 menuValidator
= new MenuValidator();
40 public boolean check() {
45 public JMenuBar
getMenuBar(){
50 public JPopupMenu
getProjectTreePopupMenu() {
51 return projectTreePopupMenu
;
55 public InsertMenuItemResult
insertProjectNavigationPopupMenuItem(final ProModAction proModAction
,
56 final MenuItemPosition menuItemPosition
) {
58 return insertProjectNavigationPopupMenuItem(
66 public InsertMenuItemResult
insertProjectNavigationPopupMenuItem(final ProModAction proModAction
,
67 final MenuItemPosition menuItemPosition
,
68 final MenuSeparator menuSeparator
) {
70 return registerPopupMenuItem(
71 ModelerModel
.MODELER_IDENTIFIER
,
79 public InsertMenuItemResult
insertPopupMenuItem(final String notationIdentifier
,
80 final ProModAction proModAction
,
81 final MenuItemPosition menuItemPosition
){
83 return insertPopupMenuItem(
92 public InsertMenuItemResult
insertPopupMenuItem(final String notationIdentifier
,
93 final ProModAction proModAction
,
94 final MenuItemPosition menuItemPosition
,
95 final MenuSeparator menuSeparator
) {
97 return registerPopupMenuItem(
106 * Inserts the popup menu item.
108 * @param notationIdentifier is the identifier of notation
109 * @param proModAction is the action to be added
110 * @param menuItemPosition is the menu items required position
111 * @param menuSeparator true if the separator should be added
112 * @return an value of InsertMenuItemResult enum saying what was the the operation result
114 private InsertMenuItemResult
registerPopupMenuItem(final String notationIdentifier
,
115 final ProModAction proModAction
,
116 final MenuItemPosition menuItemPosition
,
117 final MenuSeparator menuSeparator
) {
120 JPopupMenu popupMenu
;
122 if(ModelerModel
.MODELER_IDENTIFIER
.equals(notationIdentifier
)){
123 popupMenu
= projectTreePopupMenu
;
125 if (menuValidator
.isPopupSupported(notationIdentifier
, popupMenu
)) return InsertMenuItemResult
.POPUP_NOT_SUPPORTED
;
127 if (menuValidator
.isMenuItemPositionSet(menuItemPosition
)) return InsertMenuItemResult
.WRONG_PLACEMENT
;
129 final String hierarchicalPlacement
= menuItemPosition
.getHierarchicalPlacement();
130 final String
[] placementParts
;
132 if(hierarchicalPlacement
!= null && !hierarchicalPlacement
.isEmpty()){
133 placementParts
= hierarchicalPlacement
.split(PLACEMENT_DELIMITER
);
135 placementParts
= new String
[0];
138 // get all menus of popupMenuItems
139 final JMenuItem
[] popupMenuItems
= new JMenuItem
[popupMenu
.getComponentCount()];
140 for(int i
= 0; i
< popupMenu
.getComponentCount(); i
++){
141 popupMenuItems
[i
] = (JMenuItem
) popupMenu
.getComponent(i
);
144 return insertIntoMenu(
157 if(ModelerSession
.getNotationService().existNotation(notationIdentifier
)){
158 return ModelerSession
.getNotationService().getNotation(notationIdentifier
).addPopupMenuItem(proModAction
, menuItemPosition
, menuSeparator
, false);
160 LOG
.error("No existing notation cannot provide it's popup menu.");
171 public InsertMenuItemResult
insertMainMenuItem(final ProModAction proModAction
,
172 final MenuItemPosition menuItemPosition
,
173 final boolean checkable
){
175 return insertMainMenuItem(proModAction
, menuItemPosition
, MenuSeparator
.NONE
, checkable
);
179 public InsertMenuItemResult
insertMainMenuItem(final ProModAction proModAction
,
180 final MenuItemPosition menuItemPosition
,
181 final MenuSeparator menuSeparator
,
182 final boolean checkable
){
184 if(!menuValidator
.isMenuItemPositionSet(menuItemPosition
)) {return InsertMenuItemResult
.WRONG_PLACEMENT
;}
186 final String
[] placementParts
= menuItemPosition
.getHierarchicalPlacement().split(PLACEMENT_DELIMITER
);
188 if(!menuValidator
.areValidPlacementParts(placementParts
)) {return InsertMenuItemResult
.WRONG_PLACEMENT
;}
190 if (!menuValidator
.isItemLocationValid(placementParts
)) return InsertMenuItemResult
.WRONG_PLACEMENT
;
192 // get all menus of menuBar
193 final JMenuItem
[] menuBarItems
= new JMenuItem
[menuBar
.getMenuCount()];
194 for(int i
= 0; i
< menuBar
.getMenuCount(); i
++){
195 menuBarItems
[i
] = menuBar
.getMenu(i
);
198 return insertIntoMenu(
215 public InsertMenuItemResult
insertMainMenuItem(final ProModAction proModAction
,
216 final MenuItemPosition menuItemPosition
){
218 return insertMainMenuItem(
225 public InsertMenuItemResult
insertMainMenuItem(final ProModAction proModAction
,
226 final MenuItemPosition menuItemPosition
,
227 final MenuSeparator menuSeparator
){
229 return insertMainMenuItem(
238 * @return items in the parent menu
240 private JMenuItem
[] getMenuItems(final JMenu menu
){
241 final ArrayList
<JMenuItem
> array
= new ArrayList
<JMenuItem
>();
243 for(int i
= 0; i
< menu
.getItemCount(); i
++){
244 final Object object
= menu
.getItem(i
);
245 if((object
!= null)){
246 array
.add(menu
.getItem(i
));
250 return array
.toArray(new JMenuItem
[array
.size()]);
254 * Inserts the menu action.
256 * @param parentMenu is the parent menu where the action is supposed to be added
257 * @param parentPopupMenu is the parent popup menu where the action is supposed to be added
258 * @param proModAction is the action to be inserted
259 * @param menuSeparator the separator if one is supposed to be inserted
260 * @param menuItemPosition required menu item position
261 * @param checkable true if the menu item is supposed to be checkable
262 * @return true if no error occurs, false otherwise
264 public InsertMenuItemResult
insertAction(final JMenu parentMenu
,
265 final JPopupMenu parentPopupMenu
,
266 final ProModAction proModAction
,
267 final MenuSeparator menuSeparator
,
268 final MenuItemPosition menuItemPosition
,
269 final boolean checkable
){
271 // just one parent can be used
272 if(!menuValidator
.itemHasOnlyOneParent(parentMenu
, parentPopupMenu
)){
273 throw new IllegalArgumentException("Obscure parent menu item.");
276 if (!menuValidator
.isActionDefined(proModAction
)){ return InsertMenuItemResult
.UNDEFINED_ACTION
; }
278 final JMenuItem menuItem
= (JMenuItem
) getMenuItem(checkable
);
279 menuItem
.setAction(proModAction
);
281 // check whether the is no same menu item in the this menu structure level
282 final List
<ModelerMenuItem
> relatives
=
283 getRelatedMenuItems(parentPopupMenu
, parentMenu
, (String
) proModAction
.getValue(Action
.NAME
));
285 if(relatives
!= null){
286 LOG
.debug("Inserting a menu item to the menu hierarchy where a menu item with the same name (" +
287 proModAction
.getValue(Action
.NAME
) + ") has already been inserted.");
289 if(menuItem
instanceof ModelerMenuItem
){
290 ((ModelerMenuItem
) menuItem
).setListOfRelatives(relatives
);
292 // if there are any relatives, always group them
293 menuItemPosition
.getPositionInfo().setRelativePosition(MenuItemPosition
.PlacementStyle
.GROUPED
);
297 if(!menuValidator
.isUniqueAction(parentMenu
, parentPopupMenu
, proModAction
)){
298 return InsertMenuItemResult
.DUPLICIT_ACTION
;
301 int insertionPosition
= insertItem(menuItemPosition
, menuItem
, parentMenu
, parentPopupMenu
);
302 if(insertionPosition
< 0){
303 return InsertMenuItemResult
.WRONG_PLACEMENT
;
306 // if items are grouped, only one separator can be inserted before and one after the group
307 // this statement deals with inserting of separators AFTER the group
308 if(MenuSeparator
.AFTER
.equals(menuSeparator
)){
309 if(relatives
!= null && relatives
.size() > 0){ // grouping active
310 insertionPosition
+= relatives
.size() + 1;
314 insertSeparator(menuSeparator
, parentMenu
, parentPopupMenu
, insertionPosition
);
316 return InsertMenuItemResult
.SUCCESS
;
323 * Inserts a menu separator to the required position. Never inserts two separators next to each other.
325 * @param menuSeparator hold information whether insert separator or not and the relative position
326 * @param parentMenu is the parent menu item
327 * @param parentPopupMenu is the parent popup menu item
328 * @param insertionPosition is the actual position of the menu item that has been just added and to which is the
329 * relative position of the separator specified
331 private void insertSeparator(final MenuSeparator menuSeparator
,
332 final JMenu parentMenu
,
333 final JPopupMenu parentPopupMenu
,
334 final int insertionPosition
) {
336 // do not insert two separator next to each other
337 final Component
[] components
;
338 if(parentMenu
!= null){
339 components
= parentMenu
.getMenuComponents();
340 } else if(parentPopupMenu
!= null){
341 components
= parentPopupMenu
.getComponents();
343 LOG
.error("Obscurity in menu parents.");
347 if(menuValidator
.isSeparatorAlreadyDefinedAtPosition(components
, insertionPosition
, menuSeparator
)){
348 LOG
.info("Skipping insertion of menu separator.");
352 // insert line separator before, if obtained
353 if(MenuSeparator
.BEFORE
.equals(menuSeparator
)){
354 if(parentMenu
!= null) {
355 parentMenu
.insertSeparator(insertionPosition
);
357 parentPopupMenu
.insert(new JPopupMenu
.Separator(), insertionPosition
);
361 // insert line separator after, if obtained
362 if(MenuSeparator
.AFTER
.equals(menuSeparator
)){
363 if(parentMenu
!= null) {
364 parentMenu
.insertSeparator(insertionPosition
+ 1);
366 parentPopupMenu
.insert(new JPopupMenu
.Separator(), insertionPosition
+ 1);
373 * Performs the actual insertion of menu item.
375 * @param menuItemPosition is the specification of item's position
376 * @param menuItem is the item to be inserted
377 * @param parentMenu is the parent menu
378 * @param parentPopupMenu is the parent popupMenu
379 * @return the position to that has been the new menu item inserted
381 private int insertItem(final MenuItemPosition menuItemPosition
,
382 final JMenuItem menuItem
,
383 final JMenu parentMenu
,
384 final JPopupMenu parentPopupMenu
) {
386 int insertionPosition
= -1;
388 //insert menuItem to the position specified (exact position, FIRST, LAST possibilities)
389 if(MenuItemPosition
.PlacementStyle
.GROUPED
.equals(menuItemPosition
.getPlacementStyle())){
390 final Component
[] parentMenuItems
;
392 if (!menuValidator
.itemHasOnlyOneParent(parentMenu
, parentPopupMenu
)){
396 if(parentMenu
!= null && parentPopupMenu
== null){
397 parentMenuItems
= parentMenu
.getMenuComponents();
399 else { //(parentMenu == null && parentPopupMenu != null)
400 parentMenuItems
= parentPopupMenu
.getComponents();
404 final int position
= groupMenuItem(menuItem
, parentMenuItems
);
405 if(parentMenu
!= null){
406 parentMenu
.insert(menuItem
, position
);
408 parentPopupMenu
.insert(menuItem
, position
);
411 insertionPosition
= position
;
413 else if(menuItemPosition
.getPosition() >= 0){
414 if(parentMenu
!= null) {
415 final int position
= Math
.min(menuItemPosition
.getPosition(), parentMenu
.getItemCount());
416 parentMenu
.insert(menuItem
, position
);
417 insertionPosition
= position
;
419 else if(parentPopupMenu
!= null) {
420 final int position
= Math
.min(menuItemPosition
.getPosition(), parentPopupMenu
.getComponentCount());
421 parentPopupMenu
.insert(menuItem
, position
);
422 insertionPosition
= position
;
425 if(MenuItemPosition
.PlacementStyle
.FIRST
.equals(menuItemPosition
.getPlacementStyle())){
426 if(parentMenu
!= null) { parentMenu
.insert(menuItem
, 0); }
427 else if(parentPopupMenu
!= null) { parentPopupMenu
.insert(menuItem
, 0); }
429 insertionPosition
= 0;
431 } else if(MenuItemPosition
.PlacementStyle
.LAST
.equals(menuItemPosition
.getPlacementStyle())){
432 if(parentMenu
!= null) {
433 parentMenu
.add(menuItem
);
434 insertionPosition
= parentMenu
.getMenuComponentCount() -1;
436 else if(parentPopupMenu
!= null) {
437 parentPopupMenu
.add(menuItem
);
438 insertionPosition
= parentPopupMenu
.getComponentCount() - 1;
443 return insertionPosition
;
447 * Finds the menu item(s) having the same text under the same parent menu item and returns the position
448 * (first possible) whether the new menu item is supposed to be inserted.
450 * @param menuItem is the menu item that is supposed to be inserted
451 * @param parentMenuItems are all current menu items
452 * @return the position where the new menu item is supposed to be inserted
454 private int groupMenuItem(final JMenuItem menuItem
, final Component
[] parentMenuItems
) {
455 for(int i
= 0; i
< parentMenuItems
.length
; i
++){
456 final Component component
= parentMenuItems
[i
];
458 if(component
instanceof JMenuItem
){
459 final JMenuItem item
= (JMenuItem
) component
;
461 if(item
instanceof ModelerMenuItem
&& item
.getText().equalsIgnoreCase(menuItem
.getText())){
467 return parentMenuItems
.length
;
470 private ModelerMenuItem
getMenuItem(final boolean checkable
) {
472 return new CheckableMenuItem();
474 return new DefaultMenuItem();
479 * Finds the duplicity in menu item names and prepare the 'relatives' mechanism.
480 * If there are more than one item having the same text on it, then the set if relatives is set to an empty
481 * set. The last item having the same text on it have this set filled by references on the previous items
482 * having the same text (relatives). All menu items that do not have any duplicity in text have this set set
485 * @param parentPopupMenu is the parent popup menu item
486 * @param parentMenu is the parent menu item
487 * @param displayName is the text that is supposed to be displayed on the menu item
488 * @return the set holding references to the relatives, null if there is no text duplicity
489 * @throws IllegalArgumentException when an obscurity in parent occurs
491 private List
<ModelerMenuItem
> getRelatedMenuItems(final JPopupMenu parentPopupMenu
,
492 final JMenu parentMenu
,
493 final String displayName
) throws IllegalArgumentException
{
495 if(!menuValidator
.itemHasOnlyOneParent(parentMenu
, parentPopupMenu
)){
496 throw new IllegalArgumentException("Obscure parent menu item.");
499 final Component
[] menuComponents
;
500 if(parentPopupMenu
!= null){
501 menuComponents
= parentPopupMenu
.getComponents();
503 assert parentMenu
!= null;
504 menuComponents
= parentMenu
.getMenuComponents();
507 List
<ModelerMenuItem
> relatives
= null;
509 for(final Component component
: menuComponents
){
510 if(component
instanceof JMenuItem
){
511 final JMenuItem menuItem
= (JMenuItem
) component
;
513 if(displayName
.equals(menuItem
.getText())){
514 if(menuItem
instanceof ModelerMenuItem
){
515 final ModelerMenuItem modelerMenuItem
= (ModelerMenuItem
) menuItem
;
517 relatives
= modelerMenuItem
.getListOfRelatives();
518 modelerMenuItem
.setListOfRelatives(new LinkedList
<ModelerMenuItem
>());
520 if(relatives
== null){
521 relatives
= new LinkedList
<ModelerMenuItem
>();
524 relatives
.add(modelerMenuItem
);
534 * Inserts the menu item.
536 * @param menuLevelItems is the parent menu item
537 * @param placementParts is the hierarchical position
538 * @param proModAction is the action to be inserted
539 * @param parentMenu menu parent
540 * @param parentMenuBar menu bar
541 * @param parentPopupMenu parent popup menu
542 * @param menuSeparator menu separator info
543 * @param menuItemPosition menu item position info
544 * @param checkable true if the action is supposed to be checkable
545 * @return true id no error occurred, false otherwise
547 private InsertMenuItemResult
insertIntoMenu(
548 final JMenuItem
[] menuLevelItems
,
549 final String
[] placementParts
,
550 final ProModAction proModAction
,
551 final JMenu parentMenu
,
552 final JMenuBar parentMenuBar
,
553 final JPopupMenu parentPopupMenu
,
554 final MenuSeparator menuSeparator
,
555 final MenuItemPosition menuItemPosition
,
556 final boolean checkable
) {
558 // parent has to be clear
559 if (!menuValidator
.hasCorrectParentMenu(parentMenu
, parentMenuBar
, parentPopupMenu
)) {
560 throw new IllegalArgumentException("Obscure parent menu item.");
563 if (!menuValidator
.isActionDefined(proModAction
)) return InsertMenuItemResult
.UNDEFINED_ACTION
;
565 if(!ModelerSession
.getActionControlService().isRegisteredAction(proModAction
)){
566 LOG
.error("Unregistered action, action identifier: " + proModAction
.getActionIdentifier());
567 return InsertMenuItemResult
.NOT_REGISTERED_ACTION
;
570 if(placementParts
.length
== 0){
571 if(parentMenu
!= null){
572 return insertAction(parentMenu
, null, proModAction
, menuSeparator
, menuItemPosition
, checkable
);
573 } else if (parentPopupMenu
!= null){
574 return insertAction(null, parentPopupMenu
, proModAction
, menuSeparator
, menuItemPosition
, checkable
);
576 LOG
.error("No parent set for the menu item.");
577 return InsertMenuItemResult
.WRONG_PLACEMENT
;
580 for (final JMenuItem menuItem
: menuLevelItems
) {
581 if (menuItem
.getText().equals(placementParts
[0])) {
582 if (menuItem
instanceof JMenu
) {
583 final JMenu menu
= (JMenu
) menuItem
;
585 final JMenuItem
[] nextMenuItems
= getMenuItems(menu
);
587 return insertIntoMenu(
589 Arrays
.copyOfRange(placementParts
, 1, placementParts
.length
),
602 JMenu parent
= parentMenu
;
603 for (final String placementPart
: placementParts
) {
604 final JMenu menu
= ModelerSession
.getComponentFactoryService().createMenu(placementPart
);
606 if (parent
!= null) {
608 } else if (parentMenuBar
!= null) {
609 parentMenuBar
.add(menu
);
610 } else if (parentPopupMenu
!= null) {
611 parentPopupMenu
.add(menu
);
613 LOG
.error("There is no parent set to build the menu structure.");
614 throw new IllegalArgumentException("Obscure parent menu item.");
620 return insertAction(parent
, null, proModAction
, menuSeparator
, menuItemPosition
, checkable
);