View Javadoc

1   /*
2    * Copyright (c) 2003-2008 by Cosylab d. d.
3    *
4    * This file is part of CosyBeans-Common.
5    *
6    * CosyBeans-Common is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU General Public License as published by
8    * the Free Software Foundation, either version 3 of the License, or
9    * (at your option) any later version.
10   *
11   * CosyBeans-Common is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU General Public License for more details.
15   *
16   * You should have received a copy of the GNU General Public License
17   * along with CosyBeans-Common.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  
20  package com.cosylab.gui.components.customizer;
21  
22  import com.cosylab.gui.components.SimpleDisplayer;
23  import com.cosylab.gui.components.util.ColorHelper;
24  import com.cosylab.gui.components.util.IconHelper;
25  
26  import com.cosylab.logging.DebugLogger;
27  
28  import java.awt.BorderLayout;
29  import java.awt.Component;
30  import java.awt.FlowLayout;
31  import java.awt.Graphics;
32  import java.awt.GridBagConstraints;
33  import java.awt.GridBagLayout;
34  import java.awt.Insets;
35  import java.awt.event.ActionEvent;
36  import java.awt.event.ActionListener;
37  import java.awt.event.ComponentAdapter;
38  import java.awt.event.ComponentEvent;
39  import java.awt.event.WindowAdapter;
40  import java.awt.event.WindowEvent;
41  
42  import java.io.File;
43  import java.io.IOException;
44  import java.io.InputStream;
45  
46  import java.util.Arrays;
47  import java.util.Enumeration;
48  import java.util.HashMap;
49  import java.util.Iterator;
50  import java.util.LinkedHashMap;
51  import java.util.Map;
52  import java.util.Properties;
53  import java.util.logging.Level;
54  import java.util.logging.Logger;
55  
56  import javax.swing.Icon;
57  import javax.swing.JButton;
58  import javax.swing.JComponent;
59  import javax.swing.JDialog;
60  import javax.swing.JPanel;
61  import javax.swing.JScrollPane;
62  import javax.swing.JSplitPane;
63  import javax.swing.JTree;
64  import javax.swing.SwingUtilities;
65  import javax.swing.event.AncestorEvent;
66  import javax.swing.event.AncestorListener;
67  import javax.swing.event.TreeSelectionEvent;
68  import javax.swing.event.TreeSelectionListener;
69  import javax.swing.tree.DefaultMutableTreeNode;
70  import javax.swing.tree.DefaultTreeCellRenderer;
71  import javax.swing.tree.DefaultTreeModel;
72  import javax.swing.tree.TreeCellRenderer;
73  
74  
75  /**
76   * <code>Customizer</code> is used for displaying diferent <code>Editor</code>
77   * s of a displayer component. It is a <code>javax.swing.JPanel</code>
78   * containing a tree of availble editor aspects and a panel containing the
79   * selected aspect editors. The component also supports display via popup
80   * dialog.
81   *
82   * @author <a href="mailto:igor.krizar@cosylab.com">Igor Kriznar </a>
83   * @author <a href="mailto:jernej.kamenik@cosylab.com">Jernej Kamenik </a>
84   * @version $id$
85   * 
86   * @deprecated Use AbstractCustomizerPanel
87   */
88  public class Customizer extends JPanel
89  {
90  	private static final long serialVersionUID = 1L;
91  	private final static Logger log = DebugLogger.getLogger(Customizer.class
92  		    .getName(), Level.OFF);
93  	private JComponent displayer;
94  	private Editor editor;
95  	private Map<String, Class> editorTypes;
96  	private HashMap<Class, Editor> editors;
97  	private JPanel aspectPanel;
98  	private JTree aspectTree;
99  	private JDialog dialog;
100 	private JPanel panel;
101 	private boolean editing = false;
102 	private String lastAspect;
103 
104 	/*
105 	 * Used for rendering the tree of available aspects.
106 	 */
107 	private class AspectTreeCellRenderer extends DefaultTreeCellRenderer
108 		implements TreeCellRenderer
109 	{
110 		private static final long serialVersionUID = 1L;
111 
112 		/**
113 		 * Creates ne instance.
114 		 */
115 		public AspectTreeCellRenderer()
116 		{
117 			try {
118 				setDisabledIcon(null);
119 				setLeafIcon(new Icon() {
120 						public int getIconHeight()
121 						{
122 							return 5;
123 						}
124 
125 						public int getIconWidth()
126 						{
127 							return 5;
128 						}
129 
130 						public void paintIcon(Component c, Graphics g, int x,
131 						    int y)
132 						{
133 							g.setColor(ColorHelper.getCosyControlShadow());
134 							g.drawOval(x, y, 5, 5);
135 						}
136 					});
137 				setOpenIcon(IconHelper.createIcon("icons/navigation/Down16.gif"));
138 				setClosedIcon(IconHelper.createIcon(
139 				        "icons/navigation/Forward16.gif"));
140 
141 				//setIcon(IconHelper.createIcon("icons/navigation/Forward16.gif"));
142 			} catch (Exception e) {
143 				setClosedIcon(null);
144 				setDisabledIcon(null);
145 				setLeafIcon(null);
146 				setOpenIcon(null);
147 			}
148 		}
149 	}
150 
151 	/**
152 	 * Creates an empty Customizer with no displayer or editors.
153 	 */
154 	public Customizer()
155 	{
156 		super();
157 		editors = new HashMap<Class, Editor>();
158 		editorTypes = new LinkedHashMap<String, Class>();
159 	}
160 
161 	/**
162 	 * Creates a Customizer for provided
163 	 * <code>com.cosylab.gui.components.SimpleDisplayer</code>.
164 	 *
165 	 * @param disp the displayer to be customized.
166 	 */
167 	public Customizer(SimpleDisplayer disp)
168 	{
169 		this((JComponent)disp, disp.getEditors());
170 	}
171 
172 	/**
173 	 * Creates a Customizer for provided arbitrary displayer component with
174 	 * specified editor types.
175 	 *
176 	 * @param disp the component to be customized.
177 	 * @param editorTypes mappings of available aspects and coresponding editor
178 	 *        types
179 	 */
180 	public Customizer(JComponent disp, Map<String, Class> editorTypes)
181 	{
182 		this();
183 		setDisplayer(disp);
184 		addEditors(editorTypes);
185 	}
186 
187 	/*
188 	 * Used for testing the correctness of editor types mappings
189 	 */
190 	private void testEditors(Map map)
191 	{
192 		Iterator it = map.values().iterator();
193 
194 		while (it.hasNext()) {
195 			Object element = it.next();
196 
197 			if (!(element instanceof Class)
198 			    || !Editor.class.isAssignableFrom((Class)element)) {
199 				throw new IllegalArgumentException("Element '" + element
200 				    + "' does not implement editor interface "
201 				    + Editor.class.getName() + ".");
202 			}
203 		}
204 	}
205 
206 	/**
207 	 * Returns a live editor instance for the specified aspect.
208 	 *
209 	 * @param aspect an aspect
210 	 *
211 	 * @return instance of editor if used by this customizer  or null
212 	 *
213 	 * @throws InstantiationException if instantiation fails
214 	 * @throws IllegalAccessException if instantiation fails
215 	 */
216 	public Editor getEditor(String aspect)
217 		throws InstantiationException, IllegalAccessException
218 	{
219 		return getEditor((Class)editorTypes.get(aspect));
220 	}
221 
222 	/**
223 	 * Returns a live editor instance for the specified type, if it is
224 	 * registered in pair with any of aspects.
225 	 *
226 	 * @param type <code>Class</code> of the editor
227 	 *
228 	 * @return instance of editor if used by this customizer  or null
229 	 *
230 	 * @throws InstantiationException if instantiation fails
231 	 * @throws IllegalAccessException if instantiation fails
232 	 */
233 	public Editor getEditor(Class type)
234 		throws InstantiationException, IllegalAccessException
235 	{
236 		if (type == null) {
237 			return null;
238 		}
239 
240 		if (!editorTypes.values().contains(type)) {
241 			return null;
242 		}
243 
244 		return _getEditor(type);
245 	}
246 
247 	/*
248 	 * Get fo any type.
249 	 */
250 	private Editor _getEditor(Class type)
251 		throws InstantiationException, IllegalAccessException
252 	{
253 		if (type == null) {
254 			return null;
255 		}
256 
257 		Editor e = editors.get(type);
258 
259 		if (e != null) {
260 			return e;
261 		}
262 
263 		e = (Editor)type.newInstance();
264 
265 		editors.put(type, e);
266 
267 		return e;
268 	}
269 
270 	/*
271 	 * Initializes the custmoizer
272 	 */
273 	private void initialize()
274 	{
275 		initAspectTree();
276 		initAspectPanel(null);
277 		setLayout(new BorderLayout());
278 		add(getButtonsPanel(), BorderLayout.SOUTH);
279 		add(new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
280 		        new JScrollPane(aspectTree), new JScrollPane(aspectPanel)));
281 	}
282 
283 	/*
284 	 * Initializes the tree of available aspects
285 	 */
286 	private void initAspectTree()
287 	{
288 		if (aspectTree == null) {
289 			aspectTree = new JTree();
290 			aspectTree.setCellRenderer(new AspectTreeCellRenderer());
291 			aspectTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
292 					public void valueChanged(TreeSelectionEvent e)
293 					{
294 						Object[] aspectPath = ((DefaultMutableTreeNode)e.getNewLeadSelectionPath()
295 							.getLastPathComponent()).getUserObjectPath();
296 						String aspect = "";
297 
298 						for (int i = 1; i < aspectPath.length; i++) {
299 							aspect += "/" + (String)aspectPath[i];
300 						}
301 
302 						initAspectPanel(aspect.substring(1));
303 					}
304 				});
305 
306 			//aspectTree.setPreferredSize(new Dimension(100,100));
307 		}
308 
309 		fillAspectTree();
310 
311 		for (int i = 0; i < aspectTree.getRowCount(); i++) {
312 			aspectTree.expandRow(i);
313 		}
314 
315 		/*aspectTree.setPreferredSize(new Dimension(
316 		        (int)(aspectTree.getPreferredSize().getWidth() * 1.1),
317 		        (int)aspectTree.getPreferredSize().getHeight()));*/
318 	}
319 
320 	/*
321 	 * Fills the tree of aspects with available aspects
322 	 */
323 	private void fillAspectTree()
324 	{
325 		Iterator<String> keys = editorTypes.keySet().iterator();
326 
327 		//Arrays.sort(keys);
328 		((DefaultTreeModel)aspectTree.getModel()).setRoot(new DefaultMutableTreeNode(
329 		        ""));
330 		aspectTree.setRootVisible(false);
331 
332 		while(keys.hasNext()) {
333 			addAspectToTree(new String(keys.next()));
334 		}
335 		((DefaultTreeModel)aspectTree.getModel()).reload();
336 	}
337 
338 	/*
339 	 * Adds an aspect to the tree of aspects
340 	 */
341 	private void addAspectToTree(String aspect)
342 	{
343 		DefaultMutableTreeNode parent = (DefaultMutableTreeNode)((DefaultTreeModel)aspectTree
344 			.getModel()).getRoot();
345 
346 		while (aspect.indexOf('/') >= 0) {
347 			String parentAspect = aspect.substring(0, aspect.indexOf('/'));
348 			aspect = aspect.substring(aspect.indexOf('/') + 1);
349 
350 			Enumeration en = parent.children();
351 			DefaultMutableTreeNode tempParent = null;
352 
353 			while (en.hasMoreElements()) {
354 				tempParent = (DefaultMutableTreeNode)en.nextElement();
355 
356 				if (tempParent.getUserObject().equals(parentAspect)) {
357 					parent = tempParent;
358 
359 					break;
360 				}
361 			}
362 
363 			if (tempParent != parent) {
364 				tempParent = new DefaultMutableTreeNode(parentAspect);
365 				parent.add(tempParent);
366 				parent = tempParent;
367 			}
368 		}
369 
370 		parent.add(new DefaultMutableTreeNode(aspect));
371 	}
372 
373 	/*
374 	 * Initializes and returns the panel of buttons (OK, Apply, Cancel) that
375 	 * control the customizer
376 	 */
377 	private JComponent getButtonsPanel()
378 	{
379 		if (panel == null) {
380 			panel = new JPanel();
381 
382 			panel.setLayout(new FlowLayout(FlowLayout.RIGHT, 11, 11));
383 
384 			/* OK button */
385 			JButton button = new JButton("OK");
386 			button.addActionListener(new ActionListener() {
387 					public void actionPerformed(ActionEvent e)
388 					{
389 						commitEditing();
390 					}
391 				});
392 			panel.add(button);
393 
394 			/* Apply button */
395 			button = new JButton("Apply");
396 			button.addActionListener(new ActionListener() {
397 					public void actionPerformed(ActionEvent e)
398 					{
399 						applyEditing();
400 					}
401 				});
402 			panel.add(button);
403 
404 			/* Cancel button */
405 			button = new JButton("Cancel");
406 			button.addActionListener(new ActionListener() {
407 					public void actionPerformed(ActionEvent e)
408 					{
409 						cancelEditing();
410 					}
411 				});
412 			panel.add(button);
413 		}
414 
415 		return panel;
416 	}
417 
418 	/*
419 	 * Initializes the panel containing the editor component of the specified
420 	 * aspect
421 	 */
422 	private synchronized void initAspectPanel(final String aspect)
423 	{
424 		log.fine("Invoking display for '" + aspect + "'");
425 		lastAspect = aspect;
426 
427 		if (aspectPanel == null) {
428 			aspectPanel = new JPanel();
429 			aspectPanel.setLayout(new GridBagLayout());
430 		}
431 
432 		if (editor!=null) {
433 			editor.stopEditing();
434 		}
435 		aspectPanel.removeAll();
436 
437 		try {
438 			if (containsEditor(aspect)) {
439 				editor = getEditor(aspect);
440 				
441 
442 				/*SwingUtilities.invokeLater(new Runnable() {
443 				        public void run()
444 				        {*/
445 				/*if (ed != null) {
446 				    aspectPanel.remove(ed);
447 				}*/
448 				aspectPanel.add(editor.getEditorComponent(displayer, aspect),
449 				    new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0,
450 				        GridBagConstraints.NORTH,
451 				        GridBagConstraints.HORIZONTAL, new Insets(4, 4, 4, 4),
452 				        0, 0));
453 
454 				/*}
455 				});*/
456 			}
457 		} catch (Exception e) {
458 			System.out.println("Failed to load editor '"
459 			    + editorTypes.get(aspect) + "' for aspect '" + aspect + "': "
460 			    + e);
461 
462 			e.printStackTrace();
463 		}
464 
465 		log.fine("Calling repaint for '" + aspect + "'");
466 
467 		revalidate();
468 		repaint();
469 	}
470 
471 	/**
472 	 * Determines wether the customizer contains an editor type asociated the
473 	 * specified aspect.
474 	 *
475 	 * @param aspect
476 	 *
477 	 * @return <code>true</code> if the customizer contains an editor type
478 	 *         asociated with the aspect and <code>false</code> otherwise
479 	 */
480 	public boolean containsEditor(String aspect)
481 	{
482 		return editorTypes.containsKey(aspect);
483 	}
484 
485 	/*
486 	 * Tells all the editors to stop editing. Invoked when the dialog closes.
487 	 */
488 	private void _stopEditing()
489 	{
490 		editing = false;
491 
492 		Iterator<Editor> it = editors.values().iterator();
493 		Editor element;
494 		while (it.hasNext()) {
495 			element = it.next();
496 
497 			try {
498 				element.stopEditing();
499 			} catch (Exception e) {
500 				e.printStackTrace();
501 			}
502 		}
503 
504 		if (dialog != null) {
505 			dialog.dispose();
506 		}
507 	}
508 
509 	/*
510 	 * Invoked when Cancel button is pressed.
511 	 */
512 	private void cancelEditing()
513 	{
514 		SwingUtilities.invokeLater(new Runnable() {
515 				public void run()
516 				{
517 					_cancelEditing();
518 				}
519 			});
520 	}
521 
522 	/*
523 	 * Invoked when Cancel button is pressed.
524 	 */
525 	private void _cancelEditing()
526 	{
527 		Iterator<Editor> it = editors.values().iterator();
528 
529 		Editor element;
530 		while (it.hasNext()) {
531 			element = it.next();
532 
533 			try {
534 				element.revertSettings();
535 			} catch (Exception e) {
536 				e.printStackTrace();
537 			}
538 		}
539 
540 		_stopEditing();
541 	}
542 
543 	/*
544 	 * Uses invokeLater. Invoked when Apply button is pressed.
545 	 */
546 	private void applyEditing()
547 	{
548 		SwingUtilities.invokeLater(new Runnable() {
549 				public void run()
550 				{
551 					_applyEditing();
552 				}
553 			});
554 	}
555 
556 	/*
557 	 * Invoked when Apply button is pressed.
558 	 */
559 	private void _applyEditing()
560 	{
561 		Iterator<Editor> it = editors.values().iterator();
562 
563 		Editor element;
564 		while (it.hasNext()) {
565 			element = it.next();
566 
567 			try {
568 				element.applySettings();
569 			} catch (Exception e) {
570 				e.printStackTrace();
571 			}
572 		}
573 	}
574 
575 	/*
576 	 * Invoked when OK button is pressed.
577 	 */
578 	private void commitEditing()
579 	{
580 		SwingUtilities.invokeLater(new Runnable() {
581 				public void run()
582 				{
583 					_applyEditing();
584 					_stopEditing();
585 				}
586 			});
587 	}
588 
589 	/**
590 	 * <p>
591 	 * Shows customizer dialog centered around specified component.
592 	 * </p>
593 	 * 
594 	 * <p>
595 	 * Dialog title is composed from customiser name. Initial size is set to
596 	 * the size of customiser.
597 	 * </p>
598 	 *
599 	 * @param c a component on which customizer dialog to be centered
600 	 *
601 	 * @return a <code>javax.swing.JDialog</code> instance with customizer
602 	 */
603 	public JDialog showDialog(Component c)
604 	{
605 		if (dialog == null) {
606 			dialog = new JDialog();
607 			dialog.setTitle(getName() + " Customizer");
608 			dialog.setModal(false);
609 			dialog.addWindowListener(new WindowAdapter() {
610 					public void windowClosing(WindowEvent e)
611 					{
612 						if (editing) {
613 							cancelEditing();
614 						}
615 
616 						Iterator it = editors.values().iterator();
617 
618 						while (it.hasNext()) {
619 							Editor element = (Editor)it.next();
620 							element.stopEditing();
621 						}
622 					}
623 				});
624 			dialog.setSize(getSize().width + dialog.getInsets().left
625 			    + dialog.getInsets().right,
626 			    getSize().height + dialog.getInsets().top
627 			    + dialog.getInsets().bottom);
628 			dialog.getContentPane().setLayout(new BorderLayout());
629 			dialog.getContentPane().add(this, BorderLayout.CENTER);
630 
631 			dialog.setLocation((int)((c.getWidth() - dialog.getWidth()) / 2.0),
632 			    (int)((c.getHeight() - dialog.getHeight()) / 2.0));
633 			dialog.setLocationRelativeTo(c);
634 		}
635 
636 		initAspectPanel(lastAspect);
637 		dialog.setVisible(true);
638 		editing = true;
639 
640 		return dialog;
641 	}
642 
643 	/**
644 	 * <p>
645 	 * Shows Customizer dialog centered around customized displayer.
646 	 * </p>
647 	 * 
648 	 * <p>
649 	 * Dialog title is composed from Customizer name. Initial size is set to
650 	 * the size of Customizer.
651 	 * </p>
652 	 *
653 	 * @return a <code>JDialog</code> instance with customizer
654 	 */
655 	public JDialog showDialog()
656 	{
657 		return showDialog(displayer);
658 	}
659 
660 	/**
661 	 * <p>
662 	 * Trys to load default <code>Editor</code> s for specified displayer. Trys
663 	 * to load first default editors with <code>#looadDefaultEditors()</code>
664 	 * and then creates new Customizer.
665 	 * </p>
666 	 * 
667 	 * <p>
668 	 * Customizer tys to listen, when dispalyer is not visible any more and
669 	 * close itself.
670 	 * </p>
671 	 *
672 	 * @param disp a displayer for witch customizer is to be created
673 	 *
674 	 * @return new customizer for speficied displayer
675 	 */
676 	public static Customizer loadDefaultCustomizer(JComponent disp)
677 	{
678 		final Customizer c = new Customizer(disp, loadDefaultEditorTypes(disp));
679 
680 		// Set default name from class name.
681 		c.setName(disp.getClass().getName().substring(disp.getClass().getName()
682 		        .lastIndexOf('.') + 1));
683 
684 		// trys to get when componnet is not visible any more
685 		disp.addComponentListener(new ComponentAdapter() {
686 				public void componentHidden(ComponentEvent e)
687 				{
688 					c.cancelEditing();
689 				}
690 			});
691 
692 		disp.addAncestorListener(new AncestorListener() {
693 				public void ancestorAdded(AncestorEvent event)
694 				{
695 					// ignored
696 				}
697 
698 				public void ancestorMoved(AncestorEvent event)
699 				{
700 					// ignored
701 				}
702 
703 				public void ancestorRemoved(AncestorEvent event)
704 				{
705 					c.cancelEditing();
706 				}
707 			});
708 
709 		c.setSize(400, 300);
710 
711 		return c;
712 	}
713 
714 	/**
715 	 * Trys to load default <code>Editor</code> s for specified displayer.
716 	 * Following things tries to do in specified orther to find editors:
717 	 * 
718 	 * <ul>
719 	 * <li>
720 	 * Tryes to locate class with name that is same as displayer class with
721 	 * "Editor" at the end. if this class exists and has an array of
722 	 * <code>String</code> s "ASPECTS" as static field, than this editor si
723 	 * added to the customiser with all the listed aspects.
724 	 * </li>
725 	 * <li>
726 	 * Trys to load from system resources file "&lt;class name&gt;.editors"
727 	 * from same folder as "&lt;class name&gt;.java" file. This file must
728 	 * contain "key=value" pairs in separate lines, where key si aspect of
729 	 * editor and value is full class name of editor.
730 	 * </li>
731 	 * <li>
732 	 * If diplayer implements interface
733 	 * <code>com.cosylab.gui.components.SimpleDisplayer</code>, editors are
734 	 * taken from this interface.
735 	 * </li>
736 	 * </ul>
737 	 * 
738 	 * Latter found editors replaces existing editors in returned Map.
739 	 *
740 	 * @param disp a displayer for witch editors are to be created
741 	 *
742 	 * @return new <code>java.util.Map</code> with editor types
743 	 */
744 	public static Map<String, Class> loadDefaultEditorTypes(Object disp)
745 	{
746 		log.fine("Loading for displayer '" + disp.getClass().getName() + "'.");
747 
748 		Map<String, Class> map = new LinkedHashMap<String, Class>();
749 
750 		/* Try to guess editor from classname. */
751 		Class cl = disp.getClass();
752 
753 		boolean found = false;
754 
755 		while (!cl.equals(Object.class) && !found) {
756 			String editor = cl.getName() + "Editor";
757 
758 			try {
759 				Class c = Class.forName(editor);
760 
761 				String[] aspects = (String[])c.getField("ASPECTS").get(null);
762 
763 				if (aspects != null) {
764 					log.finer("Loaded from ASPECT field :'"
765 					    + Arrays.asList(aspects));
766 
767 					for (int i = 0; i < aspects.length; i++) {
768 						map.put(aspects[i], c);
769 					}
770 				}
771 
772 				found = true;
773 			} catch (Exception e) {
774 				// No luck.
775 			}
776 
777 			/* Try to get editors from file. */
778 			String file = cl.getName().replace('.', File.separatorChar)
779 				+ ".editors";
780 
781 			Properties p = new Properties();
782 
783 			try {
784 				InputStream in = ClassLoader.getSystemResourceAsStream(file);
785 
786 				if (in != null) {
787 					p.load(in);
788 					log.finer("Loaded from file :'" + file + "': '" + p + "'.");
789 					found = true;
790 				} else {
791 					log.finer("File not found :'" + file + "'.");
792 				}
793 			} catch (IOException e) {
794 				log.finer("Failed to load from file :'" + file + "': '" + e
795 				    + "'.");
796 				e.printStackTrace();
797 			}
798 
799 			Iterator it = p.keySet().iterator();
800 
801 			while (it.hasNext()) {
802 				String element = (String)it.next();
803 
804 				try {
805 					map.put(element, Class.forName(p.getProperty(element)));
806 				} catch (Exception e) {
807 					e.printStackTrace();
808 				}
809 			}
810 
811 			cl = cl.getSuperclass();
812 		}
813 
814 		/* Try to get editors from SimpleDisplayer. */
815 		if (disp instanceof SimpleDisplayer) {
816 			map.putAll(((SimpleDisplayer)disp).getEditors());
817 		}
818 
819 		return map;
820 	}
821 
822 	/**
823 	 * Adds map with editors to this customizer, overrides existing editors
824 	 * with same aspect. Map must contain pairs key/value, where key is aspect
825 	 * name and value is <code>Class</code> of editor.
826 	 *
827 	 * @param newEditorTypes to be added
828 	 */
829 	public void addEditors(Map<String, Class> newEditorTypes)
830 	{
831 		testEditors(newEditorTypes);
832 		editorTypes.putAll(newEditorTypes);
833 		editors.clear();
834 		initialize();
835 	}
836 
837 	/**
838 	 * Gets the dsiplayer component to be customized by this customizer.
839 	 *
840 	 * @return dispalyer being customized
841 	 */
842 	public JComponent getDisplayer()
843 	{
844 		return displayer;
845 	}
846 
847 	/**
848 	 * Sets the displayer component to be customized
849 	 *
850 	 * @param component to be customized
851 	 */
852 	public void setDisplayer(JComponent component)
853 	{
854 		displayer = component;
855 	}
856 
857 	public JPanel getAspectPanel() {
858 		return aspectPanel;
859 	}
860 
861 	public JTree getAspectTree() {
862 		return aspectTree;
863 	}
864 }
865 
866 /* __oOo__ */