001    /*
002      Copyright (C) 2001-2003 Renaud Pawlak <renaud@aopsys.com>, 
003                              Laurent Martelli <laurent@aopsys.com>
004    
005      This program is free software; you can redistribute it and/or modify
006      it under the terms of the GNU Lesser General Public License as
007      published by the Free Software Foundation; either version 2 of the
008      License, or (at your option) any later version.
009    
010      This program is distributed in the hope that it will be useful, but
011      WITHOUT ANY WARRANTY; without even the implied warranty of
012      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013      Lesser General Public License for more details.
014    
015      You should have received a copy of the GNU Lesser General Public
016      License along with this program; if not, write to the Free Software
017      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
018      USA */
019    
020    package org.objectweb.jac.aspects.gui;
021    
022    import java.util.Arrays;
023    import java.util.Collection;
024    import java.util.Iterator;
025    import java.util.List;
026    import java.util.Map;
027    import java.util.Vector;
028    import org.apache.log4j.Logger;
029    import org.objectweb.jac.aspects.integrity.RoleWrapper;
030    import org.objectweb.jac.aspects.session.SessionAC;
031    import org.objectweb.jac.core.Collaboration;
032    import org.objectweb.jac.core.Interaction;
033    import org.objectweb.jac.core.ObjectRepository;
034    import org.objectweb.jac.core.Wrappee;
035    import org.objectweb.jac.core.Wrapping;
036    import org.objectweb.jac.core.rtti.AbstractMethodItem;
037    import org.objectweb.jac.core.rtti.ClassItem;
038    import org.objectweb.jac.core.rtti.ClassRepository;
039    import org.objectweb.jac.core.rtti.CollectionItem;
040    import org.objectweb.jac.core.rtti.FieldItem;
041    import org.objectweb.jac.core.rtti.MemberItem;
042    import org.objectweb.jac.core.rtti.MethodItem;
043    import org.objectweb.jac.core.rtti.NamingConventions;
044    import org.objectweb.jac.core.rtti.RttiAC;
045    import org.objectweb.jac.util.ExtArrays;
046    import org.objectweb.jac.util.Strings;
047    
048    /**
049     * This handler manages events received from GUIs (web and swing for
050     * now). It is especially used for lists and arrays (onSelection,
051     * onView, etc ...)
052     */
053    
054    public class EventHandler implements FieldUpdate {
055        static Logger logger = Logger.getLogger("gui.events");
056        static Logger loggerDnd = Logger.getLogger("gui.dnd");
057        static Logger loggerAssoc = Logger.getLogger("associations");
058    
059        static EventHandler singleton = new EventHandler();
060    
061        public static EventHandler get() {
062            return singleton;
063        }
064    
065        /**
066         * Upcalled when a selection occurs on a field.
067         *
068         * @param context the context
069         * @param container ???
070         * @param selectedObject the selected object
071         * @param field ???
072         * @param extraOption */
073    
074        public void onSelection(
075            DisplayContext context,
076            MemberItem container,
077            Object selectedObject,
078            FieldItem field,
079            Object extraOption) 
080        {
081            onSelection(
082                context,
083                container,
084                selectedObject,
085                field,
086                extraOption,
087                false);
088        }
089    
090        /**
091         * Upcalled when a view is asked on an object.
092         *
093         * @param context the context
094         * @param container ???
095         * @param selectedObject the selected object
096         * @param field ???
097         * @param extraOption */
098    
099        public void onView(
100            DisplayContext context,
101            MemberItem container,
102            Object selectedObject,
103            FieldItem field,
104            Object extraOption) 
105        {
106            onSelection(
107                context,
108                container,
109                selectedObject,
110                field,
111                extraOption,
112                true);
113        }
114    
115        /**
116         * Upcalled when a selection occurs on a field. Displays a view of
117         * the selected object.
118         *
119         * @param context the context
120         * @param container member (reference, collection, or method) that
121         * references the selected object (the result in case of a method)
122         * @param selectedObject the selected object
123         * @param field give focus to this field in the view of the selected object
124         * @param extraOption
125         * @param forceView when true, a window containing the selected
126         * object is opened if we could not find a place where to put the
127         * new view.
128         */
129        public void onSelection(
130            DisplayContext context,
131            MemberItem container,
132            Object selectedObject,
133            FieldItem field,
134            Object extraOption,
135            boolean forceView) 
136        {
137            logger.debug("onSelection("
138                + (container != null ? container.getLongName() : "null") + ","
139                + Strings.hex(selectedObject)
140                + ",field="+ (field != null ? field.getLongName() : "null")
141                + ",force=" + forceView + ")");
142            CustomizedDisplay display = context.getDisplay();
143            ViewFactory factory = display.getFactory();
144            CustomizedView customizedView = context.getCustomizedView();
145    
146            Collaboration.get().addAttribute(GuiAC.DISPLAY_CONTEXT, context);
147    
148            // in choice view collections, nothing as to be done except a refresh
149            if (container instanceof CollectionItem) {
150                if (GuiAC.isChoiceView((CollectionItem) container)
151                    && !GuiAC.isExternalChoiceView((CollectionItem) container)) {
152                    display.refresh();
153                    return;
154                }
155            }
156    
157            MethodItem handler = null;
158            FieldItem targetField = null;
159            if (container != null) {
160                handler = GuiAC.getSelectionHandler(container);
161                targetField =
162                    (FieldItem) container.getAttribute(GuiAC.SELECTION_TARGET);
163            }
164    
165            if (customizedView == null && handler == null && targetField == null) {
166                if (forceView) {
167                    logger.debug("showing in dialog mode " + "(customizedView==null)");
168                    display.show(selectedObject);
169                } else {
170                    logger.debug("onSelection aborted " + "(customizedView==null)");
171                    display.refresh();
172                }
173                return;
174            }
175    
176            if (handler != null) {
177                try {
178                    logger.debug("invoking selectionHandler " + handler);
179                    Object result =
180                        handler.invokeStatic(
181                            new Object[] { context, container, selectedObject });
182                    if (handler.getType() != void.class && result != null) {
183                        if (handler.getType() == HandlerResult.class) {
184                            handleResult(context,(HandlerResult)result);
185                        } else {
186                            onSelection(context, null, result, null, null);
187                        }
188                    }
189                } catch (Exception e) {
190                    logger.error(
191                        "gui: invocation of event handler " + handler
192                        + " for onSelection failed with " + e);
193                    e.printStackTrace();
194                }
195            }
196    
197            if (targetField != null) {
198                selectedObject = targetField.getThroughAccessor(selectedObject);
199                logger.debug("selected target -> " + selectedObject);
200            }
201    
202            if (customizedView == null)
203                return;
204            CustomizedGUI customized = customizedView.getCustomizedGUI();
205            List targets = customized.getFieldTargets(container);
206            logger.debug("targets=" + targets);
207    
208            if (targets != null) {
209                Iterator it = targets.iterator();
210                while (it.hasNext()) {
211                                    // the target is a panelPath=
212                    Target target = (Target) it.next();
213                    logger.debug("target.path = " + target.panePath);
214                                    /*
215                                      // target.panePath must look like this <customizedId>/<paneId>
216                                      int index = target.panePath.indexOf("/");
217                                      if (index==-1) {
218                                      throw new RuntimeException("Bad pane path : "+target.panePath);
219                                      }
220                                      String customizedId = target.panePath.substring(0,index);
221                                      String paneId = target.panePath.substring(index+1);
222                                      logger.debug("customized = "+customizedId);
223                                    */
224    
225                    String paneId = target.panePath;
226                    CompositeView targetView =
227                        (CompositeView) customizedView.getView(paneId);
228    
229                    if (selectedObject != null) {
230    
231                        Collaboration.get().addAttribute(
232                            GuiAC.SMALL_VIEW,
233                            container.getAttribute(GuiAC.SMALL_TARGET_CONTAINER));
234                        try {
235                            // creating and adding the new view
236                            View view =
237                                invalidatePane(
238                                    factory,
239                                    context,
240                                    targetView,
241                                    target.viewType,
242                                    target.viewParams,
243                                    selectedObject,
244                                    extraOption);
245    
246                            if (field != null && view != null)
247                                setFocus(view, field, extraOption);
248    
249                        } finally {
250                            Collaboration.get().removeAttribute(GuiAC.SMALL_VIEW);
251                        }
252                    }
253                    //targetView.validate();
254                    maybeInvalidatePane(factory, context, customizedView, paneId);
255                }
256                display.refresh();
257            } else if (forceView) {
258                if (container != null
259                    && container.getAttribute(GuiAC.NEW_WINDOW) != null) {
260                    display.openView(selectedObject);
261                } else {
262                    display.show(selectedObject);
263                }
264            }
265    
266        }
267    
268        public void handleResult(DisplayContext context, HandlerResult hres) {
269            CustomizedView customizedView = context.getCustomizedView();
270            CustomizedDisplay display = context.getDisplay();
271            DisplayContext newContext =
272                new DisplayContext(
273                    display,
274                    hres.target != null
275                    ? hres.target
276                    : customizedView);
277            if (hres.target != null)
278                customizedView = hres.target;
279            onSelection(
280                newContext,
281                (CollectionItem) hres.container,
282                hres.object,
283                hres.field,
284                hres.extraOption);
285        }
286    
287        /**
288         * Removes the content of pane when another pane's content is
289         * changed
290         *
291         * @param selectedPaneID ID of the selected pane */
292        public void maybeInvalidatePane(
293            ViewFactory factory,
294            DisplayContext context,
295            CustomizedView customizedView,
296            String selectedPaneID) 
297        {
298            CustomizedGUI customized = customizedView.getCustomizedGUI();
299            String invalidPaneID = customized.getInvalidPane(selectedPaneID);
300            logger.debug("invalidPane " + selectedPaneID + " -> " + invalidPaneID);
301            if (invalidPaneID != null) {
302                invalidatePane(
303                    factory,
304                    context,
305                    (CompositeView) customizedView.getView(invalidPaneID),
306                    "Empty",
307                    ExtArrays.emptyStringArray,
308                    null,
309                    null);
310            }
311        }
312    
313        /**
314         * Gives focus to the view which is a field editor for a given field.
315         * @param top view to start searching from. All subviews of this
316         * view will be recursively inspected.
317         * @param field the field whose editor to search for
318         * @param option an option that will be passed when calling
319         * <code>onSetFocus()</code> on the FieldEditor.
320         * @see FieldEditor#onSetFocus(Object)
321         */
322        public void setFocus(View top, FieldItem field, Object option) {
323            logger.debug("setFocus " + top + "," + field.getLongName() + "," + option);
324            if (top instanceof CompositeView) {
325                Iterator it = ((CompositeView) top).getViews().iterator();
326                while (it.hasNext()) {
327                    View view = (View) it.next();
328                    if (view instanceof TabsView) {
329                        String[] categories = GuiAC.getCategories(field);
330                        if (categories != null && categories.length > 0) {
331                            ((TabsView) view).select(categories[0]);
332                            setFocus(
333                                ((CompositeView) view).getView(categories[0]),
334                                field,
335                                option);
336                        }
337                    } else {
338                        setFocus(view, field, option);
339                    }
340                }
341            } else if (top instanceof FieldEditor) {
342                FieldEditor editor = (FieldEditor) top;
343                if (editor.getField().equals(field)) {
344                    editor.getContext().getCustomizedView().requestFocus();
345                    editor.onSetFocus(option);
346                }
347            }
348        }
349    
350        /**
351         * Used only to test if a CompositeView contains the view that we
352         * are about to build.
353         */
354        /*
355          static class DummyView {
356          String type;
357          public void setType(String type) {
358          this.type = type;
359          }
360          public String getType() {
361          return type;
362          }
363    
364          Object[] parameters;
365          public void setParameters(Object[] parameters) {
366          this.parameters = parameters;
367          }
368          public Object[] getParameters() {
369          return parameters;
370          }
371          public boolean equals(Object o) {
372          return o instanceof ViewIdentity
373          && ((ViewIdentity) o).getType().equals(type)
374          && Arrays.equals(((ViewIdentity) o).getParameters(), parameters);
375          }
376          public int hashCode() {
377          return type.hashCode() ^ parameters.hashCode();
378          }
379    
380          public DummyView(String type, Object[] parameters) {
381          this.type = type;
382          this.parameters = parameters;
383          }
384          }
385        */
386    
387        /**
388         * Create a view for an object and display it if it is not already
389         * displayed.
390         *
391         * @return the new view or the old one
392         */
393        View invalidatePane(
394            ViewFactory factory,
395            DisplayContext context,
396            CompositeView panel,
397            String viewType,
398            String[] viewParams,
399            Object selectedObject,
400            Object extraInfo) 
401        {
402            logger.debug("invalidatePane("+panel+","
403                +viewType+Arrays.asList(viewParams)+ ","+selectedObject+")");
404            Collection comps = panel.getViews();
405            View view = null;
406            // close and remove the currently opened view in the panel
407            Object[] parameters;
408            if (selectedObject != null) {
409                parameters = ExtArrays.add(selectedObject, viewParams);
410            } else {
411                parameters = viewParams;
412            }
413            if (!panel.containsView(viewType, parameters)) {
414                logger.debug("new view " + viewType + ", extraInfo=" + extraInfo);
415                view =
416                    factory.createView(
417                        "target[?]",
418                        viewType,
419                        parameters,
420                        context);
421                if (extraInfo instanceof CollectionPosition &&
422                    GuiAC.hasSetNavBar(
423                        context.getCustomizedView().getCustomizedGUI(),
424                        ((CollectionPosition)extraInfo).getCollection())) 
425                {
426                    view.setParentView(
427                        view =
428                        factory.createView(
429                            "collectionItemView",
430                            "CollectionItemView",
431                            new Object[] {
432                                view,
433                                extraInfo,
434                                viewType,
435                                viewParams,
436                                null },
437                            context));
438                }
439                logger.debug("new view CREATED");
440                panel.addView(view, GuiAC.toString(selectedObject));
441    
442            } else {
443                Iterator i = comps.iterator();
444                while (i.hasNext()) {
445                    view = (View) i.next();
446                    if (view.equalsView(viewType,parameters)) {
447                        return view;
448                    }
449                }
450                return null;
451            }
452    
453            return view;
454        }
455    
456        /**
457         * Initialize an autocreated object by setting 
458         */
459        public static void initAutocreatedObject(
460            Object created,
461            Object substance,
462            FieldItem role) 
463        {
464            FieldItem oppositeRole =
465                (FieldItem) role.getAttribute(RttiAC.OPPOSITE_ROLE);
466            logger.debug("oppositeRole = " + oppositeRole);
467            if (oppositeRole != null) {
468                RoleWrapper.disableRoleUpdate(oppositeRole);
469                try {
470                    if (oppositeRole instanceof CollectionItem) {
471                        ((CollectionItem) oppositeRole).addThroughAdder(
472                            created,
473                            substance);
474                    } else {
475                        oppositeRole.setThroughWriter(created, substance);
476                    }
477                } catch (Exception e) {
478                    logger.error(
479                        "initAutocreatedObject(created=" + created
480                        + ",substance=" + substance
481                        + "role=" + role
482                        + "): " + e);
483                } finally {
484                    RoleWrapper.enableRoleUpdate(oppositeRole);
485                }
486            }
487        }
488    
489        /**
490         * Upcalled when a tree node is selected.
491         *
492         * @param context the display context
493         * @param node the selected tree node
494         * @param forceView if true, the subtance of the node is opened in
495         * a new window 
496         */
497        public void onNodeSelection(
498            DisplayContext context,
499            AbstractNode node,
500            boolean forceView) 
501        {
502            logger.debug("onNodeSelection " + node);
503            AbstractNode parentNode = (AbstractNode) node.getParent();
504            if (parentNode != null) {
505                // recursively send the event for the parent nodes
506                onNodeSelection(context, parentNode, false);
507            }
508            if (node instanceof ObjectNode) {
509                onSelection(
510                    context,
511                    ((ObjectNode) node).getRelation(),
512                    node.getUserObject(),
513                    null,
514                    null,
515                    forceView);
516            } else if (node instanceof RootNode) {
517                onSelection(
518                    context,
519                    null,
520                    node.getUserObject(),
521                    null,
522                    null,
523                    forceView);
524            }
525        }
526    
527        /**
528         * Upcalled when a direct invocation is performed on an object (no
529         * parameters will be asked by the GUI).
530         *
531         * @param context the display context
532         * @param substance the object that holds the method
533         * @param method the method to invoke
534         * @param parameters the parameters of the method 
535         */
536        public void onInvokeDirect(
537            DisplayContext context,
538            Object substance,
539            AbstractMethodItem method,
540            Object[] parameters) 
541        {
542            Collaboration.get().addAttribute(GuiAC.DISPLAY_CONTEXT, context);
543            method.invoke(substance, parameters);
544        }
545    
546        /**
547         * Upcalled when an invocation is performed on an object.
548         *
549         * @param context the display context
550         * @param invoke
551         * @return the thread the method was invoked in 
552         *
553         * @see #onInvoke(DisplayContext,InvokeEvent,String[],Object[])
554         * @see #onInvoke(DisplayContext,InvokeEvent,boolean,String[],Object[])
555         */
556        public InvokeThread onInvoke(
557            DisplayContext context,
558            InvokeEvent invoke)
559        {
560            return onInvoke(context, invoke, null, null);
561        }
562    
563        /**
564         * Upcalled when an invocation is performed on an object. 
565         *
566         * @param context the display context
567         * @param invoke 
568         * @param attrNames the contextual attributes names to pass
569         * @param attrValues the contextual attributes values 
570         * @return the thread the method was invoked in 
571         *
572         * @see #onInvoke(DisplayContext,InvokeEvent)
573         * @see #onInvoke(DisplayContext,InvokeEvent,boolean,String[],Object[])
574         */
575        public InvokeThread onInvoke(
576            DisplayContext context,
577            InvokeEvent invoke,
578            String[] attrNames,
579            Object[] attrValues)
580        {
581            return onInvoke(context, invoke, true, attrNames, attrValues);
582        }
583    
584        /**
585         * Invoke a method in the general case. Sets the necessary
586         * attributes in the context. The method is invoked in a new
587         * thread.
588         *
589         * @param context the display context
590         * @param invoke
591         * @param attrNames the contextual attributes names to pass
592         * @param attrValues the contextual attributes values 
593         * @return the thread the method was invoked in 
594         *
595         * @see #onInvoke(DisplayContext,InvokeEvent)
596         * @see #onInvoke(DisplayContext,InvokeEvent,String[],Object[])
597         */
598        public InvokeThread onInvoke(
599            DisplayContext context,
600            InvokeEvent invoke,
601            boolean askFormParameters,
602            String[] attrNames,
603            Object[] attrValues)
604        {
605            logger.debug("onInvoke(" + context + "," + invoke + ")");
606            CustomizedDisplay display = context.getDisplay();
607            if (attrNames == null)
608                attrNames = ExtArrays.emptyStringArray;
609            if (attrValues == null)
610                attrValues = ExtArrays.emptyStringArray;
611            Class[] parameterTypes = invoke.getMethod().getParameterTypes();
612            int parametersLeft = parameterTypes.length;
613            Object[] parameters = invoke.getParameters();
614            if (parameters == null) {
615                parameters = new Object[parameterTypes.length];
616            } else if (parameters.length < parameterTypes.length) {
617                // If there are not enough parameters,
618                // we assume the first ones are missing
619                parametersLeft -= parameters.length;
620                Object[] tmp = new Object[parameterTypes.length];
621                System.arraycopy(
622                    parameters,
623                    0,
624                    tmp,
625                    parameterTypes.length - parameters.length,
626                    parameters.length);
627                parameters = tmp;
628            }
629            if (parameters.length > 0
630                && parameterTypes[0] == DisplayContext.class) {
631                parametersLeft--;
632                parameters[0] = context;
633            }
634            invoke.setParameters(parameters);
635            // Get the session id from the current context
636            Object sid = Collaboration.get().getAttribute(SessionAC.SESSION_ID);
637            if (parametersLeft == 0) {
638                logger.debug("Invoking " + invoke);
639                String[] names = new String[2 + attrNames.length];
640                Object[] values = new Object[2 + attrNames.length];
641                names[0] = GuiAC.DISPLAY_CONTEXT;
642                values[0] = context;
643                names[1] = SessionAC.SESSION_ID;
644                values[1] = sid;
645                System.arraycopy(attrNames, 0, names, 2, attrNames.length);
646                System.arraycopy(attrValues, 0, values, 2, attrNames.length);
647                return 
648                    InvokeThread.run(
649                        invoke,
650                        null,
651                        null,
652                        names,
653                        values);
654            } else {
655                //new CallingBox( this, substance, method );
656                logger.debug("Invoking " + invoke +
657                    " (ask for parameters is on, "
658                    + parametersLeft + " parameters left)");
659    
660                String[] names = new String[4 + attrNames.length];
661                Object[] values = new Object[4 + attrNames.length];
662                names[0] = GuiAC.DISPLAY_CONTEXT;
663                values[0] = context;
664                names[1] = SessionAC.SESSION_ID;
665                values[1] = sid;
666                names[2] = GuiAC.ASK_FOR_PARAMETERS;
667                names[3] = GuiAC.INVOKED_METHOD;
668                // Do not ask for parameters if all values are not null
669                if (ExtArrays.indexOf(parameters, null) != -1 && askFormParameters) {
670                    values[2] = invoke.getMethod().getConcreteMethod();
671                    values[3] = invoke.getMethod();
672                }
673    
674                System.arraycopy(attrNames, 0, names, 4, attrNames.length);
675                System.arraycopy(attrValues, 0, values, 4, attrNames.length);
676                return 
677                    InvokeThread.run(
678                        invoke,
679                        null,
680                        null,
681                        names,
682                        values);
683            }
684        }
685    
686        /**
687         * Invoke a method and waits for the result (and returns it).
688         *
689         * @param context the display context
690         * @param substance the object that holds the method
691         * @param method the method to invoke
692         * @param parameters the method's parameters */
693    
694        public Object onInvokeSynchronous(
695            DisplayContext context,
696            InvokeEvent invoke)
697        {
698            logger.debug("onInvokeSynchronous(" + context + "," + invoke + ")");
699            CustomizedDisplay display = context.getDisplay();
700            Class[] parameterTypes = invoke.getMethod().getParameterTypes();
701            int parametersLeft = parameterTypes.length;
702            Object[] parameters = invoke.getParameters();
703            if (parameters == null) {
704                parameters = new Object[parameterTypes.length];
705            } else if (parameters.length < parameterTypes.length) {
706                // If there are not enough parameters,
707                // we assume the first ones are missing
708                parametersLeft -= parameters.length;
709                Object[] tmp = new Object[parameterTypes.length];
710                System.arraycopy(
711                    parameters,
712                    0,
713                    tmp,
714                    parameterTypes.length - parameters.length,
715                    parameters.length);
716                parameters = tmp;
717            }
718            if (parameters.length > 0
719                && parameterTypes[0] == DisplayContext.class) {
720                parametersLeft--;
721                parameters[0] = context;
722            }
723    
724            String[] names;
725            Object[] values;
726    
727            // Get the session id from the current context
728            Object sid = Collaboration.get().getAttribute(SessionAC.SESSION_ID);
729            if (parametersLeft == 0) {
730                logger.debug("Invoking " + invoke);
731                names =
732                    new String[] { GuiAC.DISPLAY_CONTEXT, SessionAC.SESSION_ID };
733                values = new Object[] { context, sid };
734            } else {
735                //new CallingBox( this, substance, method );
736                logger.debug("Invoking " + invoke
737                    + " (ask for parameters is on, "
738                    + parametersLeft + " parameters left)");
739                names =
740                    new String[] {
741                        GuiAC.DISPLAY_CONTEXT,
742                        SessionAC.SESSION_ID,
743                        GuiAC.ASK_FOR_PARAMETERS };
744                values = new Object[] { context, sid, invoke.getMethod() };
745            }
746    
747            invoke.setParameters(parameters);
748            InvokeThread thread =
749                new InvokeThread(
750                    invoke,
751                    null,
752                    null,
753                    names,
754                    values);
755            thread.start();
756            return thread.getReturnValue();
757        }
758    
759        /**
760         * This method is upcalled when an object is added to a collection.
761         *
762         * @param context the display context
763         * @param substance the object that holds the collection
764         * @param collection the collection 
765         */
766        public void onAddToCollection(
767            DisplayContext context,
768            AddEvent add)
769        {
770            onAddToCollection(context, add, false);
771        }
772    
773        /**
774         * This method is upcalled when an object is added to a collection.
775         *
776         * @param context the display context
777         * @param substance the object that holds the collection
778         * @param collection the collection 
779         * @param noAutoCreate if true, does not auto create the object to
780         * add, whatever the configuration for the collection.
781         */
782        public void onAddToCollection(
783            DisplayContext context,
784            AddEvent add,
785            boolean noAutoCreate)
786        {
787            logger.debug("onAddToCollection "+add
788                + "; noAutoCreate=" + noAutoCreate);
789            CollectionItem collection = add.getCollection();
790            MethodItem addMethod = collection.getAdder();
791            Collaboration collab = Collaboration.get();
792            FieldItem oppositeRole =
793                (FieldItem) collection.getAttribute(RttiAC.OPPOSITE_ROLE);
794            loggerAssoc.debug("opposite_role = " + oppositeRole);
795            if (oppositeRole instanceof CollectionItem) {
796                loggerAssoc.debug("Ignoring collection oppositeRole " + oppositeRole);
797                oppositeRole = null;
798            }
799    
800            try {
801                if (addMethod != null) {
802                    logger.debug("Invoking add method " + addMethod.getName()
803                        + " on collection owner");
804    
805                    if (noAutoCreate) {
806                        ClassItem type =
807                            ClassRepository.get().getClass(
808                                addMethod.getParameterTypes()[0]);
809                        onInvoke(
810                            context,
811                            new InvokeEvent(
812                                add.getSource(),
813                                add.getSubstance(),
814                                addMethod),
815                            new String[] { GuiAC.NO_AUTO_CREATE, GuiAC.VIEW },
816                            new Object[] { type, this });
817                    } else {
818                        onInvoke(
819                            context,
820                            new InvokeEvent(
821                                add.getSource(),
822                                add.getSubstance(),
823                                addMethod),
824                            new String[] { GuiAC.VIEW, GuiAC.OPPOSITE_ROLE },
825                            new Object[] { this, oppositeRole });
826                    }
827                } else {
828                    logger.debug("No adder for " + collection);
829                    context.getDisplay().refresh();
830                                    /*
831                                      getMethod = collection.getGetter();
832                                      if (getMethod!=null)
833                                      MethodItem method = null;
834                                      Object c;
835                                      try {
836                                      c = getMethod.invoke(substance,ExtArrays.emptyObjectArray);
837                                      } catch (Exception e) {
838                                      logger.error("Getting collection with "+getMethod.getName()+
839                                      "failed : "+e);
840                                      return;
841                                      }
842                                      ClassItem cl = ClassRepository.get().getClass(c.getClass());
843                                      if (collection.isMap())
844                                      method = cl.getMethod("put(Object,Object)");
845                                      else
846                                      method = cl.getMethod("add(Object)");
847                                      Log.trace("gui","Invoking "+method.getName()+" on collection itself");
848                                      GuiAC.invoke((Display)parent,method,c,collection);
849                                      }
850                                    */
851                }
852            } finally {
853                collab.removeAttribute(GuiAC.OPPOSITE_ROLE);
854            }
855        }
856    
857        /**
858         * This method is upcalled when an object is removed from a collection.
859         *
860         * @param context the display context
861         * @param remove
862         * @param askFormParameters wether to to display an input box for
863         * the parameters of the remover method
864         */
865        public void onRemoveFromCollection(
866            DisplayContext context,
867            RemoveEvent remove,
868            boolean askFormParameters)
869        {
870            CollectionItem collection = remove.getCollection();
871            Object selected = remove.getRemoved();
872            MethodItem remover = collection.getRemover();
873            if (remover != null) {
874                logger.debug("Invoking remove method "
875                    + remover.getName()
876                    + " on collection owner");
877    
878                if (collection.isMap() && selected instanceof Map.Entry) {
879                                    // We don't want to call the remover with an entry as the
880                                    // parameter
881                    selected = ((Map.Entry) selected).getKey();
882                }
883    
884                try {
885                    if (selected != null)
886                        onInvoke(
887                            context,
888                            new InvokeEvent(
889                                remove.getSource(),
890                                remove.getSubstance(),
891                                remover,
892                                new Object[] {selected}),
893                            false,
894                            ExtArrays.emptyStringArray,
895                            ExtArrays.emptyObjectArray);
896                    else
897                        onInvoke(
898                            context,
899                            new InvokeEvent(
900                                remove.getSource(),
901                                remove.getSubstance(),
902                                remover,
903                                new Object[] {selected}),
904                            askFormParameters,
905                            ExtArrays.emptyStringArray,
906                            ExtArrays.emptyObjectArray);
907    
908                                    /**
909                                     * Close all views where the removed element is displayed
910                                     */
911                    CustomizedView customizedView = context.getCustomizedView();
912                    if (customizedView != null) {
913                        CustomizedGUI customized =
914                            customizedView.getCustomizedGUI();
915                        List targets = customized.getFieldTargets(collection);
916    
917                        if (targets != null) {
918                            Iterator it = targets.iterator();
919                            while (it.hasNext()) {
920                                Target target = (Target) it.next();
921                                CompositeView targetView =
922                                    (CompositeView) customizedView.getView(
923                                        target.panePath);
924                                if (targetView == null)
925                                    continue;
926    
927                                Object[] parameters = new Object[] {selected};
928                                Iterator it2 = targetView.getViews().iterator();
929                                while (it2.hasNext()) {
930                                    View view = (View) it2.next();
931    
932                                    View view2 = null;
933                                    if (view
934                                        instanceof AbstractCollectionItemView) {
935                                        view2 = view;
936                                        view =
937                                            ((AbstractCollectionItemView) view)
938                                            .getView();
939                                    }
940    
941                                    if (view.equalsView(view.getType(),parameters)) {
942                                        if (view2 != null) {
943                                            view.close(true);
944                                            view = view2;
945                                        }
946                                        ((CompositeView)view.getParentView())
947                                            .removeView(view,true);
948                                        // Should removeView() do the close() ???
949                                        view.close(true);
950    
951                                        it2 = targetView.getViews().iterator();
952                                    }
953                                }
954                            }
955                        }
956                    }
957                } catch (Exception e) {
958                    logger.error(
959                        "failed to remove "
960                        + ((selected != null) ? selected : "element")
961                        + " from collection");
962                    e.printStackTrace();
963                }
964            } else {
965                context.getDisplay().showError(
966                    "Cannot Remove",
967                    "No remover method available for collection " + collection);
968                // TODO: handling of arrays
969            }
970        }
971    
972        /**
973         * This method is upcalled when an object has to be created in an
974         * object chooser.
975         *
976         * @param context the display context
977         * @param cli the class of the object to create
978         * @param substance
979         * @param field a field to which the created object will be "added". May be null.
980         */
981        public Object onCreateObject(
982            DisplayContext context,
983            ClassItem cli,
984            Object substance,
985            FieldItem field)
986        {
987            logger.debug("onCreateObject(" + cli.getName() + "," + field + ")");
988    
989            Vector classes = new Vector(cli.getChildren());
990            classes.add(cli);
991    
992            Object newInstance = null;
993            try {
994                
995                Collaboration collab = Collaboration.get();
996    
997                Object fieldChoice = field!=null ? field.getAttribute(GuiAC.FIELD_CHOICE) : null;
998                if (fieldChoice instanceof CollectionItem) {
999                    CollectionItem choice = (CollectionItem)fieldChoice;
1000                    collab.addAttribute(GuiAC.AUTOCREATE_REASON, 
1001                                        new Interaction(null,
1002                                                        (Wrappee)choice.getSubstance(substance),
1003                                                        choice.getAdder(),new Object[1]));
1004                    try {
1005                        newInstance = cli.newInstance();
1006                    } finally {
1007                        collab.removeAttribute(GuiAC.AUTOCREATE_REASON);
1008                    }
1009                    onInvokeDirect(
1010                        context,
1011                        choice.getSubstance(substance),
1012                        choice.getAdder(),
1013                        new Object[] {newInstance});
1014                } else {
1015                    newInstance = cli.newInstance();
1016                }
1017    
1018                if (substance != null && field != null) {
1019                    MethodItem init = GuiAC.getInitiliazer(field);
1020                    if (init != null) {
1021                        init.invoke(substance, new Object[] { newInstance });
1022                    }
1023                }
1024    
1025                FieldItem oppositeRole = setOppositeRole(field);
1026                //collab.addAttribute(GuiAC.EMBEDDED_EDITORS, Boolean.TRUE);
1027                collab.addAttribute(GuiAC.AUTO_CREATION, Boolean.TRUE);
1028                try {
1029                    boolean ok =
1030                        context.getDisplay().showModal(
1031                            newInstance,
1032                            "Object",
1033                            new String[] {GuiAC.AUTOCREATE_VIEW},
1034                            "New " + NamingConventions.getShortClassName(cli),
1035                            "Fill the needed information and validate.",
1036                            context.getWindow(),
1037                            true,
1038                            true,
1039                            false);
1040    
1041                    if (!ok) {
1042                        logger.debug("Creation of " + cli.getName() + " cancelled");
1043                        ObjectRepository.delete((Wrappee) newInstance);
1044                        newInstance = null;
1045                    }
1046                } finally {
1047                    if (oppositeRole != null)
1048                        collab.removeAttribute(GuiAC.OPPOSITE_ROLE);
1049                                    //collab.removeAttribute(GuiAC.EMBEDDED_EDITORS);
1050                    collab.removeAttribute(GuiAC.AUTO_CREATION);
1051                }
1052            } catch (Exception e) {
1053                logger.error("onCreateObject("+cli.getName()+","+
1054                             substance+","+field+") failed",e);
1055            }
1056            return newInstance;
1057        }
1058    
1059        public void onDropObject(
1060            DisplayContext context,
1061            Object target,
1062            Object droppedObject,
1063            Object source,
1064            boolean copy)
1065        {
1066            logger.debug("onDropObject(" + target + "," + droppedObject + ")");
1067            ClassItem cli = ClassRepository.get().getClass(target);
1068            Class objectType = droppedObject.getClass();
1069            CollectionItem[] cols = cli.getCollections();
1070            for (int i = 0; i < cols.length; i++) {
1071                CollectionItem c = cols[i];
1072                loggerDnd.debug("checking collection " + c);
1073                MethodItem adder = c.getAdder();
1074                if (adder != null) {
1075                    loggerDnd.debug("types(" + adder.getParameterTypes()[0] + "," + objectType + ")");
1076                    if (adder.getParameterTypes()[0] == objectType) {
1077                        if (!copy && GuiAC.isRemovable(c) && GuiAC.isAddable(c)) {
1078                            loggerDnd.debug("moving object...");
1079                            c.removeThroughRemover(source, droppedObject);
1080                            c.addThroughAdder(target, droppedObject);
1081                        } else if (
1082                            copy && GuiAC.isRemovable(c) && GuiAC.isAddable(c)) {
1083                            loggerDnd.debug("copying object...");
1084                            Wrapping.clone(droppedObject);
1085                            c.addThroughAdder(target, droppedObject);
1086                        }
1087                    }
1088                }
1089            }
1090        }
1091    
1092        public void fieldUpdated(
1093            Object substance,
1094            FieldItem field,
1095            Object value,
1096            Object param)
1097        {
1098            CompositeView view = (CompositeView) param;
1099            logger.debug("rebuilding view " + view);
1100            view.removeAllViews(false);
1101            GenericFactory.fillObjectView(
1102                view,
1103                ClassRepository.get().getClass(substance),
1104                "default",
1105                substance);
1106        }
1107    
1108        public static FieldItem setOppositeRole(FieldItem field) {
1109            if (field!=null) {
1110                FieldItem oppositeRole =
1111                    (FieldItem)field.getAttribute(RttiAC.OPPOSITE_ROLE);
1112                if (oppositeRole instanceof CollectionItem) {
1113                    loggerAssoc.debug("Ignoring collection oppositeRole of "+
1114                                      field.getLongName() + ": "+oppositeRole);
1115                    return null;
1116                } else {
1117                    loggerAssoc.debug("opposite_role of "+field.getLongName()+" = " + oppositeRole);
1118                    Collaboration.get().addAttribute(GuiAC.OPPOSITE_ROLE, oppositeRole);
1119                    return oppositeRole;
1120                }
1121            } else {
1122                return null;
1123            }
1124        }
1125    }