001    /**
002     * Created by IntelliJ IDEA.
003     * User: Wei Wang
004     * Date: Nov 27, 2002
005     * Time: 3:50:11 PM
006     */
007    
008    package EVolve.visualization;
009    
010    import EVolve.data.*;
011    import EVolve.Scene;
012    import javax.swing.*;
013    import javax.imageio.*;
014    import java.awt.event.*;
015    import java.awt.image.BufferedImage;
016    import java.awt.*;
017    import java.util.*;
018    import java.io.*;
019    
020    public abstract class Visualization {
021        public static final int SELECT_TIME_FRAME = 0x0001;
022        public static final int SELECT_OCCURRED_ENTITIES = 0x0010;
023        public static final int SELECT_ALL_ENTITIES = 0x0100;
024        public static final int SELECT_X_AXIS = 0x1000;
025        public static final int SELECT_Y_AXIS = 0x0100;
026        public static final int SELECT_ALL_X_AXIS = 0x0010;
027        public static final int SELECT_ALL_Y_AXIS = 0x0001;
028    
029    
030        protected String name; // name of the visualization
031        protected EVolve.Window window; // window where the visualization is displayed
032    
033        protected VisualizationDefinition definition; // definition of the visualization
034        protected ElementDefinition subjectDefinition; // definition of the subject
035    
036        protected Dimension[] dimension; // dimension of the visualization
037        protected JPanel panel; // panel to draw the visualization
038    
039        protected JDialog dialog; // configuration dialog
040        protected JPanel panelConfiguration; // configuration panel
041        protected JComboBox comboSubject; // subject selection
042        protected JComboBox[] comboDimension; // dimension selection
043        private JTextField textName; // name input
044    
045        protected JPopupMenu popup; // popup menu
046        private JMenu menuSort;
047        private JMenu[] menuDimension;
048        protected JMenuItem[][] itemSort;
049        private JMenuItem itemConfigure;
050        private JMenuItem itemSelect;
051        private JMenuItem itemSave;
052        private ReferenceDimension[] referenceDimension;
053        private static int vizIDPool = 0;
054        private int linkageId;
055        private VisualizationFactory factory = null;
056    
057        protected ElementDefinition[] elementDefinition; // definition of elements that can be used as subject
058        protected DataFilter[][][] dataFilter; // data filters that can be used by the dimensions
059        protected int mouseX, mouseY;
060    
061        public Visualization() {
062            dimension = createDimension();
063            panel = createPanel();
064            panelConfiguration = null;
065            dialog = null;
066            linkageId = vizIDPool++;
067            addPopupTrigger(panel);
068        }
069    
070        public String getName() {
071            return name;
072        }
073    
074        /**
075         * Sets the name of the visualization.
076         *
077         * @param  name  name of the visualization
078         */
079        public void setName(String name) {
080            this.name = name;
081        }
082    
083        /**
084         * Sets the window of the visualization.
085         *
086         * @param  window  window of the visualization
087         */
088        public void setWindow(EVolve.Window window) {
089            this.window = window;
090        }
091    
092        /**
093         * Gets the panel of the visualization.
094         *
095         * @return  panel of the visualization
096         */
097        public JPanel getPanel() {
098            return panel;
099        }
100    
101        /**
102         * Gets the additional configuration panel.
103         *
104         * @return  the additional configuration panel, null if not necessary
105         */
106        protected JPanel createConfigurationPanel() {
107            return null; // by default, return null, each visualization can override this
108        }
109    
110        protected void createMenu() {
111            popup = new JPopupMenu();
112    
113            itemConfigure = new JMenuItem("Configure...");
114            itemConfigure.setMnemonic(KeyEvent.VK_C);
115            itemConfigure.addActionListener(new ActionListener() {
116                public void actionPerformed(ActionEvent e) {
117                    configure();
118                }
119            });
120            popup.add(itemConfigure);
121    
122            itemSelect = new JMenuItem("Make Selection");
123            itemSelect.setMnemonic(KeyEvent.VK_M);
124            itemSelect.addActionListener(new ActionListener() {
125                public void actionPerformed(ActionEvent e) {
126                    makeSelection();
127                }
128            });
129            popup.add(itemSelect);
130    
131            menuSort = new JMenu("Sort");
132            menuSort.setMnemonic(KeyEvent.VK_S);
133            menuSort.setEnabled(false);
134            popup.add(menuSort);
135    
136            ArrayList tempList = new ArrayList();
137            for (int i = 0; i < dimension.length; i++) {
138                if (dimension[i] instanceof ReferenceDimension) {
139                    tempList.add(new Integer(i));
140                }
141            }
142    
143            referenceDimension = new ReferenceDimension[tempList.size()];
144            menuDimension = new JMenu[tempList.size()];
145            itemSort = new JMenuItem[tempList.size()][];
146            for (int i = 0; i < tempList.size(); i++) {
147                int j = ((Integer)(tempList.get(i))).intValue();
148                referenceDimension[i] = (ReferenceDimension)(dimension[j]);
149                menuDimension[i] = new JMenu(definition.getDimensionDefinition()[j].getName());
150                menuSort.add(menuDimension[i]);
151            }
152    
153            itemSave = new JMenuItem("Save...");
154            itemSave.setMnemonic(KeyEvent.VK_V);
155            itemSave.addActionListener(new ActionListener() {
156                public void actionPerformed(ActionEvent e) {
157                    save();
158                }
159            });
160            popup.add(itemSave);
161        }
162    
163        private void showPopup(MouseEvent e) {
164            mouseX = e.getX();
165            mouseY = e.getY();
166            Rectangle rect = e.getComponent().getBounds();
167            Rectangle rect2 = popup.getBounds();
168            int posX= mouseX, posY = mouseY;
169            if (mouseY+rect2.height > rect.height)
170                posY = posY - rect2.height;
171            if (mouseX+rect2.width>rect.width)
172                posX = posX - rect2.width;
173                popup.show(e.getComponent(), posX, posY);
174        }
175    
176        /**
177         * Sets the definition of the visualization, called by visualization manager after the visualization is created.
178         *
179         * @param  definition  definition of the visualization
180         */
181        public void setDefinition(VisualizationDefinition definition) {
182            panelConfiguration = createConfigurationPanel();
183    
184            this.definition = definition;
185    
186            elementDefinition = Scene.getDataManager().getElementDefinition(definition);
187    
188            dataFilter = new DataFilter[elementDefinition.length][definition.getDimensionDefinition().length][];
189            for (int i = 0; i < dataFilter.length; i++) {
190                for (int j = 0; j < dataFilter[i].length; j++) {
191                    dataFilter[i][j] = Scene.getDataManager().getDataFilter(elementDefinition[i],definition.getDimensionDefinition()[j].getProperty());
192                    for (int k = 0; k < dataFilter[i][j].length; k++) {
193                        assert (((dataFilter[i][j][k].getTargetType() != -1) && (dimension[j] instanceof ReferenceDimension)) || ((dataFilter[i][j][k].getTargetType() == -1) && (dimension[j] instanceof ValueDimension))) : "Incompatible dimension type.";
194                    }
195                }
196            }
197    
198            subjectDefinition = elementDefinition[0];
199            for (int i = 0; i < dimension.length; i++) {
200                dimension[i].setDataFilter(dataFilter[0][i][0]);
201            }
202    
203            updateConfiguration();
204    
205            createMenu();
206        }
207    
208        /**
209         * Gets the definition of the subject.
210         *
211         * @return  definition of the subject
212         */
213        public ElementDefinition getSubjectDefinition() {
214            return subjectDefinition;
215        }
216    
217        /**
218         * Configures the visualization.
219         */
220        public void configure() {
221            if (dialog == null) {
222                createDialog();
223            }
224            dialog.pack();
225            Scene.getUIManager().showDialog(dialog, dialog.getWidth(), dialog.getHeight());
226        }
227    
228        /**
229         * Save visualizations as disk file
230         */
231        public void save() {
232            BufferedImage image = new BufferedImage(panel.getWidth(), panel.getHeight(), BufferedImage.TYPE_INT_RGB);
233            panel.paint(image.getGraphics());
234            ImageWriter writer = (ImageWriter)(ImageIO.getImageWritersByFormatName("png").next());
235    
236            JFileChooser fc = new JFileChooser(Scene.getUIManager().getLastResultDir());
237            fc.setFileFilter(new javax.swing.filechooser.FileFilter() {
238                public String getDescription() {
239                    return "PNG Files";
240                }
241    
242                public boolean accept(File f) {
243                    return f.getName().substring(f.getName().lastIndexOf('.') + 1).toLowerCase().equals("png");
244                }
245            });
246    
247            if (fc.showSaveDialog(Scene.getFrame()) == JFileChooser.APPROVE_OPTION) {
248                File file = fc.getSelectedFile();
249                Scene.getUIManager().setLastResultDir(file.getPath());
250                try {
251                    writer.setOutput(ImageIO.createImageOutputStream(file));
252                    writer.write(image);
253                } catch (IOException e) {}
254            }
255        }
256    
257        /**
258         * Creates the configuration dialog.
259         */
260        protected void createDialog() {
261            dialog = new JDialog(Scene.getFrame(), "Configure", true);
262    
263            JPanel panelTitle = new JPanel(new FlowLayout());
264            dialog.getContentPane().add(panelTitle, BorderLayout.NORTH);
265    
266            panelTitle.add(new JLabel("Title: "));
267    
268            textName = new JTextField(name, 12);
269            panelTitle.add(textName);
270    
271            JPanel panelMain = new JPanel(new BorderLayout());
272            panelMain.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Choose subject & dimensions"));
273            dialog.getContentPane().add(panelMain, BorderLayout.CENTER);
274    
275            JPanel panelName = new JPanel(new GridLayout(definition.getDimensionDefinition().length + 1, 1, 5, 5));
276            panelMain.add(panelName, BorderLayout.WEST);
277    
278            panelName.add(new JLabel(" Subject:      "));
279            for (int i = 0; i < definition.getDimensionDefinition().length; i++) {
280                panelName.add(new JLabel(" " + definition.getDimensionDefinition()[i].getName() + ":      "));
281            }
282    
283            JPanel panelCombo = new JPanel(new GridLayout(definition.getDimensionDefinition().length + 1, 1, 5, 5));
284            panelMain.add(panelCombo, BorderLayout.CENTER);
285    
286            comboSubject = new JComboBox();
287            for (int i = 0; i < elementDefinition.length; i++) {
288                comboSubject.addItem(elementDefinition[i].getName());
289            }
290            comboSubject.addActionListener(new ActionListener() {
291                public void actionPerformed(ActionEvent e) {
292                    updateComboSubject();
293                }
294            });
295            panelCombo.add(comboSubject);
296    
297            comboDimension = new JComboBox[definition.getDimensionDefinition().length];
298            for (int i = 0; i < comboDimension.length; i++) {
299                comboDimension[i] = new JComboBox();
300                comboDimension[i].addActionListener(new ActionListener() {
301                    public void actionPerformed(ActionEvent e) {
302                        updateComboDimension();
303                    }
304                });
305                panelCombo.add(comboDimension[i]);
306            }
307    
308            updateComboSubject();
309    
310            if (panelConfiguration != null) {
311                panelMain.add(panelConfiguration, BorderLayout.SOUTH);
312            }
313    
314            JPanel panelButton = new JPanel(new FlowLayout());
315            dialog.getContentPane().add(panelButton, BorderLayout.SOUTH);
316    
317            JButton buttonApply = new JButton("Apply");
318            buttonApply.addActionListener(new ActionListener() {
319                public void actionPerformed(ActionEvent e) {
320                    dialogApply();
321                }
322            });
323            panelButton.add(buttonApply);
324    
325            JButton buttonCancel = new JButton("Cancel");
326            buttonCancel.addActionListener(new ActionListener() {
327                public void actionPerformed(ActionEvent e) {
328                    dialogCancel();
329                }
330            });
331            panelButton.add(buttonCancel);
332    
333            //*************
334            for (int i=0; i<comboSubject.getItemCount(); i++) {
335                if ((comboSubject.getItemAt(i)).equals(subjectDefinition.getName())) {
336                    comboSubject.setSelectedIndex(i);
337                    break;
338                }
339            }
340            for (int i=0;i<comboDimension.length;i++) {
341                for (int j =0 ; j<comboDimension[i].getItemCount();j++) {
342                    if (comboDimension[i].getItemAt(j).equals(dimension[i].getDataFilter().getName())) {
343                        comboDimension[i].setSelectedIndex(j);
344                        break;
345                    }
346                }
347            }
348        }
349    
350        /**
351         * Updates the subject combo-box.
352         */
353        private void updateComboSubject() {
354            comboSubject.setToolTipText(elementDefinition[comboSubject.getSelectedIndex()].getDescription());
355            for (int i = 0; i < comboDimension.length; i++) {
356                comboDimension[i].removeAllItems();
357                for (int j = 0; j < dataFilter[comboSubject.getSelectedIndex()][i].length; j++) {
358                    comboDimension[i].addItem(dataFilter[comboSubject.getSelectedIndex()][i][j].getName());
359                }
360            }
361            updateComboDimension();
362        }
363    
364        /**
365         * Updates the dimension combo-boxes.
366         */
367        private void updateComboDimension() {
368            for (int i = 0; i < comboDimension.length; i++) {
369                if (comboDimension[i].getSelectedIndex() != -1) {
370                    comboDimension[i].setToolTipText(dataFilter[comboSubject.getSelectedIndex()][i][comboDimension[i].getSelectedIndex()].getDescription());
371                }
372            }
373        }
374    
375        /**
376         * Button "Apply" is clicked.
377         */
378        private void dialogApply() {
379            dialog.setVisible(false);
380            subjectDefinition = elementDefinition[comboSubject.getSelectedIndex()];
381            name = textName.getText();
382            if (name.indexOf(subjectDefinition.getName()) == -1)
383                window.setTitle(name + " - " + subjectDefinition.getName());
384            else
385                window.setTitle(name);
386            for (int i = 0; i < dimension.length; i++) {
387                dimension[i].setDataFilter(dataFilter[comboSubject.getSelectedIndex()][i][comboDimension[i].getSelectedIndex()]);
388                if (dimension[i] instanceof ReferenceDimension) ((ReferenceDimension)dimension[i]).clearEntityMap();
389            }
390            updateConfiguration();
391            menuSort.setEnabled(false);
392        }
393    
394        /**
395         * Button "Cancel" is clicked.
396         */
397        private void dialogCancel() {
398            dialog.setVisible(false);
399        }
400    
401        public void updateMenu() {
402            for (int i = 0; i < referenceDimension.length; i++) {
403                ArrayList comparatorList = referenceDimension[i].getComparator();
404                itemSort[i] = new JMenuItem[comparatorList.size()];
405                menuDimension[i].removeAll();
406                for (int j = 0; j < itemSort[i].length; j++) {
407                    itemSort[i][j] = new JMenuItem(((EntityComparator)(comparatorList.get(j))).getName());
408                    itemSort[i][j].addActionListener(new ActionListener() {
409                        public void actionPerformed(ActionEvent e) {
410                            selectComparator(e);
411                        }
412                    });
413                    menuDimension[i].add(itemSort[i][j]);
414                }
415            }
416    
417            ArrayList menuOptional = createOptionalMenu();
418            if (menuOptional == null ) return;
419            for (int i=0; i<menuOptional.size(); i++)
420                popup.add((JMenuItem)menuOptional.get(i));
421        }
422    
423        protected ArrayList createOptionalMenu() {
424            return null;
425        }
426    
427        public void selectComparator(ActionEvent e) {
428            for (int i = 0; i < itemSort.length; i++) {
429                for (int j = 0; j < itemSort[i].length; j++) {
430                    if (itemSort[i][j] == e.getSource()) {
431                        referenceDimension[i].selectComparator(j);
432                        sort();
433                        return;
434                    }
435                }
436            }
437        }
438    
439        protected void addPopupTrigger(Component component) {
440            component.addMouseListener(new MouseAdapter() {
441                public void mouseReleased(MouseEvent e) {
442                    if (e.isPopupTrigger()) {
443                        showPopup(e);
444                    }
445                }
446    
447                public void mousePressed(MouseEvent e) {
448                    if (e.isPopupTrigger()) {
449                        showPopup(e);
450                    }
451                }
452            });
453        }
454    
455        public void enableSortMenu() {
456            menuSort.setEnabled(true);
457        }
458    
459        /**
460         * Creates the dimensions.
461         *
462         * @return  the dimensions
463         */
464        public abstract Dimension[] createDimension();
465    
466        /**
467         * Creates available selection menu items.
468         *
469         * @return  the array of menu items
470         */
471        public abstract JMenuItem[] createSelectionMenuItem();
472    
473        /**
474         * Creates the panel.
475         *
476         * @return  the panel
477         */
478        protected abstract JPanel createPanel();
479    
480        /**
481         * Updates the configuration.
482         */
483        protected abstract void updateConfiguration();
484    
485        /**
486         * Gets ready for receiving data.
487         */
488        public abstract void preVisualize();
489    
490        /**
491         * Receives data.
492         *
493         * @param  data  the data
494         */
495        protected abstract void receiveElement(Element element);
496    
497        /**
498         * Visualizes after receiving data.
499         */
500        public abstract void visualize();
501    
502        /**
503         * sorting the visualization with selected comparator
504         */
505        public abstract void sort();
506    
507        /**
508         * according to user selectiong, get a data subset
509         */
510        public abstract void makeSelection();
511    
512        /**
513         *
514         * @return a Hash map contains all current visualization's configure
515         */
516        public HashMap getCurrentConfigure() {
517            HashMap configure = new HashMap();
518    
519            configure.put("Factory",getFactory());
520            configure.put("Subject",subjectDefinition);
521            configure.put("Name",name);
522            configure.put("Interval", new Integer(-1));
523            configure.put("Options", ";not using any options");
524    
525            return configure;
526        }
527    
528        /**
529         * following are all interfaces needed for predefined viz
530         */
531        public void autoUpdateConfiguration(HashMap config) {
532    
533            int selectedSubjectId = -1;
534            Dimension [] predefinedDimension;
535    
536            for (int i=0; i< elementDefinition.length; i++) {
537                if (elementDefinition[i].getName().equals(((ElementDefinition)config.get("Subject")).getName())) {
538                    subjectDefinition = elementDefinition[i];
539                    selectedSubjectId = i;
540                    break;
541                }
542            }
543    
544            predefinedDimension = (Dimension[])config.get("Dimension");
545            for (int i=0; i<dimension.length; i++) {
546                for (int j=0; j< dataFilter[selectedSubjectId][i].length;j++) {
547                    if (dataFilter[selectedSubjectId][i][j].getName().equals(predefinedDimension[i].getDataFilter().getName())) {
548                        dimension[i].setDataFilter(dataFilter[selectedSubjectId][i][j]);
549                        break;
550                    }
551                }
552            }
553    
554            updateConfiguration();
555            window.setTitle(name);
556            menuSort.setEnabled(false);
557        }
558    
559        public EVolve.Window getWindow() {
560            return window;
561        }
562    
563        public void setFactory(VisualizationFactory factory) {
564            this.factory = factory;
565        }
566    
567        public VisualizationFactory getFactory() {
568            return factory;
569        }
570    
571        public void autoSave(String path, String dataFn) {
572            BufferedImage image = new BufferedImage(panel.getWidth(), panel.getHeight(), BufferedImage.TYPE_INT_RGB);
573            panel.paint(image.getGraphics());
574            ImageWriter writer = (ImageWriter)(ImageIO.getImageWritersByFormatName("png").next());
575    
576            try {
577                RandomAccessFile file = new RandomAccessFile((path+File.separator+
578                        dataFn.substring(dataFn.lastIndexOf(File.separator)+1,dataFn.length()-4)+".png"),"rw");
579                writer.setOutput(ImageIO.createImageOutputStream(file));
580                writer.write(image);
581            } catch (IOException e) {
582                System.out.println("writing file error.");
583            }
584    
585        }
586    
587        /**
588         * following methods are used in unify/overlap viz
589         */
590        public abstract ReferenceDimension getLinkableDimension(int dim);
591    
592        public int getVisualizationID() {
593            return linkageId;
594        }
595    
596        public Dimension[] getDimension() {
597            return dimension;
598        }
599    
600        public VisualizationDefinition getDefinition() {
601            return definition;
602        }
603    
604        public int getxMax() {
605            return -1;
606        }
607    
608        public AutoImage getImage() {
609            return null;
610        }
611    
612        public void setImage(AutoImage image) {
613            return;
614        }
615    
616        public void cleanup() {
617            dimension = null;
618            comboDimension = null;
619            menuDimension = null;
620            itemSort = null;
621            referenceDimension = null;
622            elementDefinition = null;
623            dataFilter = null;
624        }
625    
626        protected int switchOption(boolean turn_on, int option, int add_on_option) {
627            int returnVal;
628            if (turn_on)
629                returnVal = option | add_on_option;
630            else
631                returnVal = option & ~add_on_option;
632            return returnVal;
633        }
634    }