KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > jac > aspects > gui > GenericFactory


1 /*
2   Copyright (C) 2002-2003 Renaud Pawlak <renaud@aopsys.com>,
3                           Laurent Martelli <laurent@aopsys.com>
4   
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU Lesser General Public License as
7   published by the Free Software Foundation; either version 2 of the
8   License, or (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful, but
11   WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13   Lesser General Public License for more details.
14
15   You should have received a copy of the GNU Lesser General Public
16   License along with this program; if not, write to the Free Software
17   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18   USA */

19
20 package org.objectweb.jac.aspects.gui;
21
22 import java.lang.reflect.Array;
23 import java.lang.reflect.Modifier;
24 import java.util.Arrays;
25 import java.util.Collection;
26 import java.util.Hashtable;
27 import java.util.Iterator;
28 import java.util.Map;
29 import java.util.Vector;
30 import org.apache.log4j.Logger;
31 import org.objectweb.jac.core.Collaboration;
32 import org.objectweb.jac.core.Naming;
33 import org.objectweb.jac.core.ObjectRepository;
34 import org.objectweb.jac.core.Wrappee;
35 import org.objectweb.jac.core.rtti.AbstractMethodItem;
36 import org.objectweb.jac.core.rtti.ClassItem;
37 import org.objectweb.jac.core.rtti.ClassRepository;
38 import org.objectweb.jac.core.rtti.CollectionItem;
39 import org.objectweb.jac.core.rtti.FieldItem;
40 import org.objectweb.jac.core.rtti.MetaItem;
41 import org.objectweb.jac.core.rtti.MethodItem;
42 import org.objectweb.jac.core.rtti.MixinMethodItem;
43 import org.objectweb.jac.core.rtti.RttiAC;
44 import org.objectweb.jac.core.rtti.VirtualClassItem;
45 import org.objectweb.jac.util.Classes;
46 import org.objectweb.jac.util.Enum;
47 import org.objectweb.jac.util.ExtArrays;
48 import org.objectweb.jac.util.ExtBoolean;
49 import org.objectweb.jac.util.NotInCollectionPredicate;
50 import org.objectweb.jac.util.Predicate;
51 import org.objectweb.jac.util.Stack;
52 import org.objectweb.jac.util.Strings;
53
54 /**
55  * This class implements static methods that generically create GUI
56  * items. Depending on the actual view factory, the created items are
57  * for SWING, WEB or other GUI. */

58
59 public class GenericFactory {
60     static Logger logger = Logger.getLogger("gui.generic");
61     static Logger loggerAssoc = Logger.getLogger("associations");
62
63     /**
64      * Creates a view on an object.
65      *
66      * @param factory the used factory
67      * @param context the display context (passed to ceated sub-item so
68      * that they know displays and customized)
69      * @param viewName name of the view to build
70      * @param substance the object to build a view of */

71
72     public static View createObjectView(
73         ViewFactory factory,
74         DisplayContext context,
75         String viewName,
76         Object substance)
77     {
78         logger.debug("createObjectView("
79                 + factory + "," + Strings.hex(substance) + "," + viewName + ")");
80
81         if (substance == null) {
82             return factory.createView("null", "Empty", context);
83         }
84
85         if (substance instanceof Number
86             || substance instanceof Boolean
87             || substance instanceof Character
88             || substance instanceof String) {
89             return factory.createView(
90                 substance.getClass().getName(),
91                 "Field",
92                 new Object[] { substance, null, null },
93                 context);
94         }
95
96         Class substance_type = substance.getClass();
97         ClassItem cli = ClassRepository.get().getClass(substance_type);
98
99         CompositeView view =
100             (CompositeView) factory.createCompositeView(
101                 cli.getName(),
102                 "ObjectView",
103                 context);
104
105         fillObjectView(view, cli, viewName, substance);
106         Collection dependencies = GuiAC.getDependentFields(cli);
107         if (dependencies != null) {
108             Iterator it = dependencies.iterator();
109             while (it.hasNext()) {
110                 FieldItem field = (FieldItem) it.next();
111                 Utils.registerField(substance, field, EventHandler.get(), view);
112             }
113         }
114         return view;
115     }
116
117     public static void fillObjectView(
118         CompositeView view,
119         ClassItem cli,
120         String viewName,
121         Object substance)
122     {
123         ObjectView objectView = GuiAC.getView(cli, viewName);
124         ViewFactory factory = view.getFactory();
125         DisplayContext context = view.getContext();
126         String[] categories = (String[]) cli.getAttribute(GuiAC.CATEGORIES);
127         String[] icons = GuiAC.getCategoriesIcons(cli);
128         String[] labels = GuiAC.getCategoriesLabels(cli);
129         if (labels == null)
130             labels = categories;
131
132         view.setStyle((String) cli.getAttribute(GuiAC.STYLE));
133         view.setDescription(GuiAC.getDescription(substance, "<b>", "</b>"));
134
135         logger.debug("categories = "
136                 + (categories != null
137                     ? Arrays.asList(categories).toString()
138                     : "null"));
139
140         if (categories == null) {
141             view.addView(
142                 createObjectView(
143                     factory,
144                     context,
145                     objectView,
146                     substance,
147                     null));
148         } else {
149             TabsView tabbedPane =
150                 (TabsView) factory.createCompositeView(
151                     cli.getName(),
152                     "Tabbed",
153                     context);
154             for (int i = 0; i < categories.length; i++) {
155                 logger.debug("add tab for category " + categories[i]);
156
157                 String icon;
158                 if ((icons != null) && (Array.getLength(icons) > i))
159                     icon = icons[i];
160                 else
161                     icon = null;
162
163                 CompositeView tab =
164                     createObjectView(
165                         factory,
166                         context,
167                         objectView,
168                         substance,
169                         categories[i]);
170                 if (!compositeViewIsEmpty(tab))
171                     tabbedPane.addTab(tab, labels[i], icon);
172                 else
173                     logger.debug("tab[" + categories[i] + "] is empty");
174             }
175             view.addView((CompositeView) tabbedPane);
176         }
177     }
178
179     /**
180      * Creates a view of an object (containing no tabs).
181      *
182      * @param factory the used factory
183      * @param context the display context (passed to ceated sub-item so
184      * that they know displays and customized)
185      * @param substance the viewed object */

186
187     public static View createObjectViewNoTab(
188         ViewFactory factory,
189         DisplayContext context,
190         Object substance)
191     {
192         logger.debug("createObjectViewNoTab("
193                 + factory
194                 + ","
195                 + Strings.hex(substance)
196                 + ")");
197
198         if (substance == null) {
199             return factory.createView(
200                 substance.getClass().getName(),
201                 "Empty",
202                 context);
203         } else {
204             return createObjectView(
205                 factory,
206                 context,
207                 GuiAC.getView(
208                     ClassRepository.get().getClass(substance),
209                     "default"),
210                 substance,
211                 null);
212         }
213     }
214
215     /**
216      * Returns true is the CompositeView contains a view other than
217      * CompositeView.
218      */

219     public static boolean compositeViewIsEmpty(CompositeView view) {
220         Iterator it = view.getViews().iterator();
221         while (it.hasNext()) {
222             View subView = (View) it.next();
223             if (!(subView instanceof CompositeView))
224                 return false;
225             else if (!compositeViewIsEmpty((CompositeView) subView))
226                 return false;
227         }
228         return true;
229     }
230
231     /**
232      * Create a view of an object, including only the attributes of a
233      * category.
234      *
235      * @param factory the ViewFactory
236      * @param context the DisplayContext
237      * @param substance the object to build the view of
238      * @param category the category; if null, all fields are shown
239      * @return a CompositeView representing the object
240      */

241     protected static CompositeView createObjectView(
242         ViewFactory factory,
243         DisplayContext context,
244         ObjectView view,
245         Object substance,
246         String category)
247     {
248         logger.debug("createObjectView("
249                 + factory + "," + Strings.hex(substance) + ","
250                 + category+ "," + view.getName() + ")");
251         CompositeView resultPane =
252             factory.createCompositeView(
253                 substance.getClass().getName() + "[" + category + "]",
254                 "Container",
255                 new Object[] { new Integer(Constants.VERTICAL)},
256                 context);
257
258         Class substance_type = substance.getClass();
259         ClassItem cli = ClassRepository.get().getClass(substance_type);
260
261         CompositeView fieldpane =
262             factory.createCompositeView(
263                 "fields",
264                 "ParameterContainer",
265                 new Object[] { Boolean.TRUE },
266                 context);
267         // EditorContainer editorContainer = (EditorContainer)fieldpane;
268

269         // primitive fields and references
270

271         FieldItem[] fields = null;
272         fields = view.getAttributesOrder();
273         if (fields == null)
274             fields = cli.getFields();
275
276         boolean embedded = view.getName().equals(GuiAC.AUTOCREATE_VIEW);
277         logger.debug("attributes_order = " + Arrays.asList(fields));
278         FieldItem[] groups = (FieldItem[]) cli.getAttribute(GuiAC.LINE_BREAKS);
279         int curgroup = 0;
280
281         CompositeView subFieldPane = null;
282         FieldItem oppositeRole =
283             (FieldItem) Collaboration.get().getAttribute(GuiAC.OPPOSITE_ROLE);
284         loggerAssoc.debug("opposite role = " + oppositeRole);
285         boolean first = true;
286         for (int i = 0; i < fields.length; i++) {
287             if (groups != null
288                 && curgroup < groups.length
289                 && groups[curgroup] == fields[i]) {
290                 if (subFieldPane != null) {
291                     fieldpane.addView(subFieldPane);
292                 }
293                 subFieldPane =
294                     factory.createCompositeView(
295                         "subField[" + i + "]",
296                         "Container",
297                         new Object[] { new Integer(Constants.HORIZONTAL)},
298                         context);
299                 first = true;
300                 curgroup++;
301             }
302             FieldItem field = fields[i];
303             //if(field==null) continue;
304
if (GuiAC.isMemberInCategory(field, category)
305                 && GuiAC.isVisible(substance, field)
306                 && !field.isStatic()
307                 && !field.equals(oppositeRole)) {
308                 CompositeView pane =
309                     subFieldPane != null ? subFieldPane : fieldpane;
310                 try {
311                     if (!first && subFieldPane != null)
312                         subFieldPane.addHorizontalStrut(10);
313                     pane.addView(
314                         getFieldPane(
315                             factory,
316                             context,
317                             substance,
318                             view,
319                             field,
320                             embedded));
321                     first = false;
322                 } catch (Exception e) {
323                     logger.error(
324                         "Failed to build view for field \""+field+"\"", e);
325                 }
326             }
327         }
328         if (subFieldPane != null) {
329             fieldpane.addView(subFieldPane);
330         }
331
332         resultPane.addView(fieldpane);
333
334         logger.debug("getMethodsPane for " + cli);
335
336         Collection meths = new Vector();
337         //if (Collaboration.get().getAttribute(GuiAC.AUTO_CREATION) == null) {
338
MethodItem[] ms = view.getMethodsOrder();
339         if (ms != null) {
340             meths = Arrays.asList(ms);
341         }
342         //}
343

344         logger.debug("methods = " + meths);
345
346         CompositeView methodsPane =
347             getMethodsPane(factory, context, substance, meths, category, view);
348         if (methodsPane != null) {
349             logger.debug("adding methodPane");
350             resultPane.addView(methodsPane);
351         }
352
353         return resultPane;
354
355     }
356
357     public static View getFieldPane(
358         ViewFactory factory,
359         DisplayContext context,
360         Object substance,
361         ObjectView objectView,
362         FieldItem field,
363         boolean embedded)
364     {
365         GuiAC.pushGraphicContext(field);
366         try {
367             MemberItemView memberView = GuiAC.getView(field, objectView.getName());
368             if (field.isReference()) {
369                 return getReferenceFieldPane(
370                     factory,
371                     context,
372                     substance,
373                     field,
374                     embedded,
375                     objectView,memberView);
376             } else if (field.isPrimitive()
377                     || factory.hasViewerFor(field.getTypeItem().getName())) {
378                 return getPrimitiveFieldPane(
379                     factory,
380                     context,
381                     substance,
382                     field,
383                     objectView,
384                     memberView,
385                     embedded);
386             } else if (field instanceof CollectionItem) {
387                 return getCollectionPane(
388                     factory,
389                     context,
390                     substance,
391                     objectView,
392                     (CollectionItemView) memberView,
393                     (CollectionItem) field);
394             }
395         } finally {
396             GuiAC.popGraphicContext();
397         }
398         return null;
399     }
400
401     /**
402      * Instantiates a viewer for a value of a field and add it to a
403      * container.
404      */

405     protected static boolean getViewer(
406         Object substance,
407         FieldItem field,
408         Object value,
409         CompositeView container,
410         ViewFactory factory,
411         DisplayContext context)
412     {
413         Stack types = new Stack();
414         types.push(ClassRepository.get().getClass(field.getType()));
415         if (value != null) {
416             ClassItem actualClass = ClassRepository.get().getClass(value);
417             if (types.safeTop()!=actualClass)
418                 types.push(actualClass);
419         }
420
421         Enum enum = GuiAC.getEnum(field);
422         if (enum!=null) {
423             types.push(ClassRepository.get().getVirtualClass("Enum"));
424         }
425
426         MetaItem type = RttiAC.getFieldType(field,substance);
427         if (type!=null && types.safeTop()!=type)
428             types.push(type);
429
430         logger.debug("types = " + types);
431         boolean foundViewer = false;
432         while (!types.empty()) {
433             type = (MetaItem) types.pop();
434             if (factory.hasViewerFor(type.getName())) {
435                 container.addView(
436                     factory.createView(
437                         field.getName(),
438                         type.getName(),
439                         new Object[] { value, substance, field },
440                         context));
441                 foundViewer = true;
442                 break;
443             }
444         }
445         return foundViewer;
446     }
447
448     /**
449      * Returns a view of a primitive field. It contains a label and the
450      * value of the field.
451      *
452      * @param factory the view factory
453      * @param context the display context
454      * @param substance the object the field is part of
455      * @param field the field item
456      * @param objectView the object view that contains the field view
457      * @param memberView the view to build the field for
458      * @param embedded use embbeded editors
459      */

460     protected static View getPrimitiveFieldPane(
461         ViewFactory factory,
462         DisplayContext context,
463         Object substance,
464         FieldItem field,
465         ObjectView objectView,
466         MemberItemView memberView,
467         boolean embedded)
468     {
469         logger.debug("primitive field : " + substance + "," + field.getName());
470
471         /*
472         Boolean attrValue = (Boolean)field.getAttribute(GuiAC.BORDER);
473         boolean border = attrValue!=null && attrValue.booleanValue();
474         */

475
476         CompositeView container =
477             factory.createCompositeView(
478                 "field[" + field.getName() + "]",
479                 "Container",
480                 new Object[] { new Integer(Constants.HORIZONTAL)},
481                 context);
482         Border border = GuiAC.getBorder(field);
483         if (border != null) {
484             border.setTitle(GuiAC.getLabel(field));
485             container.setViewBorder(border);
486         }
487         container.setStyle((String) field.getAttribute(GuiAC.STYLE));
488         Boolean displayLabel =
489             (Boolean) field.getAttribute(GuiAC.DISPLAY_LABEL);
490         boolean useEditor =
491             GuiAC.isEditable(substance, field)
492             && memberView.isEmbeddedEditor(embedded)
493             && !objectView.isReadOnly();
494         if ((border == null)
495             && (displayLabel == null
496                 || (displayLabel != null && displayLabel.booleanValue()))) {
497             container.addView(
498                 factory.createView(
499                     GuiAC.getLabel(field) + ": ",
500                     "Label",
501                     context));
502         }
503
504         FieldEditor editor = null;
505         if (useEditor) {
506             MethodItem setter = field.getSetter();
507             if (setter != null) {
508                 try {
509                     editor =
510                         getEditorComponent(
511                             factory,
512                             context,
513                             field.getSubstance(substance),
514                             setter,
515                             0,
516                             true,
517                             null);
518                     context.addEditor(editor);
519                     container.addView(editor);
520                 } catch (Exception e) {
521                     // If the editor failed, we'll try a normal view
522
logger.error("Failed to build editor component for "+
523                                  substance+"."+field.getName(),e);
524                 }
525             }
526         }
527         if (editor==null) {
528             Object value = field.getThroughAccessor(substance);
529             if (!getViewer(substance,
530                            field,
531                            value,
532                            container,
533                            factory,
534                            context))
535             {
536                 Enum enum = GuiAC.getEnum(field);
537                 if (enum != null) {
538                     container.addView(
539                         factory.createView(
540                             field.getName(),
541                             "Enum",
542                             new Object[] { value, enum, substance, field },
543                             context));
544                 } else {
545                     container.addView(
546                         factory.createView(
547                             field.getName(),
548                             "Field",
549                             new Object[] { value, substance, field },
550                             context));
551                 }
552             }
553
554             if (GuiAC.isEditable(substance, field) && !objectView.isReadOnly()) {
555                 container.addView(
556                     getEditButton(factory, substance, field, context));
557             }
558         }
559         return container;
560     }
561
562     /**
563      * Build a view containing a label for the name of the field, and
564      * the view of the reference.
565      *
566      * @param factory the view factory
567      * @param context the display context
568      * @param substance the object the field is part of
569      * @param field the field item
570      * @param embedded use embbeded editors
571      */

572     static protected View getReferenceFieldPane(
573         ViewFactory factory,
574         DisplayContext context,
575         Object substance,
576         FieldItem field,
577         boolean embedded,
578         ObjectView objectView,
579         MemberItemView memberView)
580     {
581         logger.debug("reference field : " + field.getName());
582         /*
583         Boolean attrValue = (Boolean)field.getAttribute(GuiAC.BORDER);
584         boolean border = attrValue!=null && attrValue.booleanValue();
585         */

586         CompositeView container =
587             factory.createCompositeView(
588                 "field[" + field.getName() + "]",
589                 "Container",
590                 new Object[] { new Integer(Constants.HORIZONTAL)},
591                 context);
592
593         Border border = GuiAC.getBorder(field);
594         if (border != null) {
595             border.setTitle(GuiAC.getLabel(field));
596             container.setViewBorder(border);
597         }
598
599         container.setStyle((String) field.getAttribute(GuiAC.STYLE));
600         Boolean displayLabel =
601             (Boolean) field.getAttribute(GuiAC.DISPLAY_LABEL);
602
603         if ((border == null)
604             && (displayLabel == null
605                 || (displayLabel != null && displayLabel.booleanValue()))) {
606
607             container.addView(
608                 factory.createView(
609                     GuiAC.getLabel(field) + ": ",
610                     "Label",
611                     context));
612         }
613
614         if ((memberView != null && memberView.isEmbedded()) && !embedded) {
615             FieldItem oppositeRole =
616                 (FieldItem) field.getAttribute(RttiAC.OPPOSITE_ROLE);
617             if (oppositeRole instanceof CollectionItem) {
618                 loggerAssoc.debug("Ignoring collection oppositeRole " + oppositeRole);
619                 oppositeRole = null;
620             }
621             Collaboration.get().addAttribute(GuiAC.OPPOSITE_ROLE, oppositeRole);
622             try {
623                 logger.debug("embedded view for " + field);
624                 container.addView(
625                     factory.createObjectView(
626                         "embbeded " + field.getName(),
627                         field.getThroughAccessor(substance),
628                         substance,
629                         field,
630                         context));
631             } finally {
632                 Collaboration.get().addAttribute(GuiAC.OPPOSITE_ROLE, null);
633             }
634         } else {
635             if (memberView.isEmbeddedEditor(embedded)
636                 && GuiAC.isEditable(substance, field)
637                 && !objectView.isReadOnly())
638             {
639                 MethodItem setter = field.getSetter();
640                 FieldEditor editor =
641                     getEditorComponent(
642                         factory,
643                         context,
644                         field.getSubstance(substance),
645                         setter,
646                         0,
647                         true,
648                         null);
649                 container.addView(editor);
650                 context.addEditor(editor);
651             } else {
652                 //Object value = field.getThroughAccessor(substance);
653
//if (!getViewer(substance,field,value,container,factory,context)) {
654
View refView =
655                     factory.createView(
656                         field.getName(),
657                         "Reference",
658                         new Object[] {
659                             field.getThroughAccessor(substance),
660                             field.getSubstance(substance),
661                             field.getField()},
662                         context);
663                 if (refView instanceof LinkGenerator)
664                     ((LinkGenerator)refView).setEnableLinks(objectView.areLinksEnabled());
665                 container.addView(refView);
666
667                 if (GuiAC.isEditable(field.getSubstance(substance),field.getField())
668                     && !objectView.isReadOnly()) {
669                     container.addView(
670                         getEditButton(
671                             factory,
672                             field.getSubstance(substance),
673                             field.getField(),
674                             context));
675                 }
676                 //}
677
}
678         }
679         return container;
680     }
681
682     /**
683      * Adds choices within a container containing a combobox and sort
684      * them.
685      *
686      * @param choice combo box model to fill
687      * @param type type of objects to fill the model with
688      * @param field associated field item
689      * @param nullAllowed boolean telling wether the add null to themodel
690      * @param nullLabel if nullAllowed==true, the label to use for the null value (if not null)
691      * @param predicate if not null, only add objects which match this
692      * predicate
693      */

694     static protected void addChoices(
695         ComboBoxModel choice,
696         ClassItem type,
697         Enum enum,
698         Object substance,
699         FieldItem field,
700         boolean nullAllowed,
701         String nullLabel,
702         Predicate predicate)
703     {
704         // use JacObjects.getObjects(type) instead
705
Collection all_objects = null;
706         logger.debug("ObjectChooser.type = " + type
707                 + "; predicate=" + predicate
708                 + "; field=" + field
709                 + "; nullAllowed=" + nullAllowed);
710         if (field != null) {
711             Object fieldChoice = field.getAttribute(GuiAC.FIELD_CHOICE);
712             logger.debug(" fieldChoice = "+fieldChoice);
713             if (fieldChoice instanceof MethodItem) {
714                 try {
715                     all_objects =
716                         (Collection) (((MethodItem) fieldChoice)
717                             .invoke(null, ExtArrays.emptyObjectArray));
718                 } catch (Exception e) {
719                     e.printStackTrace();
720                 }
721             } else if (fieldChoice instanceof CollectionItem) {
722                 all_objects = ((CollectionItem)fieldChoice).getActualCollectionThroughAccessor(substance);
723             }
724             choice.setType(type);
725         }
726
727         if (enum!=null) {
728             Iterator it = enum.getValues().iterator();
729             while (it.hasNext()) {
730                 String str = (String) it.next();
731                 choice.addObject(new Integer(enum.string2int(str)), str);
732             }
733             choice.setType(type);
734             if (nullAllowed) {
735                 choice.setNullLabel(nullLabel);
736                 choice.addObject(null);
737             }
738         } else {
739
740             if (all_objects == null) {
741                 all_objects = ObjectRepository.getObjects(type);
742                 choice.setType(type);
743             }
744             if (nullAllowed) {
745                 choice.setNullLabel(nullLabel);
746                 choice.addObject(null);
747             }
748             
749             Iterator i = all_objects.iterator();
750             while (i.hasNext()) {
751                 Object object = i.next();
752                 if (predicate == null || predicate.apply(object)) {
753                     choice.addObject(object);
754                 }
755             }
756             choice.sort();
757         }
758     }
759
760     /**
761      * Constructs an edit button for reference views.
762      */

763     static protected View getEditButton(
764         ViewFactory factory,
765         Object substance,
766 &