1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package de.desy.acop.displayers.selector;
24
25 import java.awt.Component;
26 import java.awt.Dimension;
27 import java.awt.Font;
28 import java.awt.GridBagConstraints;
29 import java.awt.GridBagLayout;
30 import java.awt.GridLayout;
31 import java.awt.Insets;
32 import java.awt.datatransfer.DataFlavor;
33 import java.awt.datatransfer.Transferable;
34 import java.awt.datatransfer.UnsupportedFlavorException;
35 import java.awt.event.ActionEvent;
36 import java.awt.event.ActionListener;
37 import java.awt.event.FocusAdapter;
38 import java.awt.event.FocusEvent;
39 import java.awt.event.KeyAdapter;
40 import java.awt.event.KeyEvent;
41 import java.awt.event.MouseAdapter;
42 import java.awt.event.MouseEvent;
43 import java.awt.event.MouseMotionAdapter;
44 import java.beans.Beans;
45 import java.beans.Customizer;
46 import java.beans.PropertyChangeEvent;
47 import java.beans.PropertyChangeListener;
48 import java.io.IOException;
49 import java.util.ArrayList;
50 import java.util.StringTokenizer;
51 import java.util.concurrent.ExecutorService;
52 import java.util.concurrent.LinkedBlockingQueue;
53 import java.util.concurrent.RejectedExecutionHandler;
54 import java.util.concurrent.ThreadPoolExecutor;
55 import java.util.concurrent.TimeUnit;
56
57 import javax.swing.AbstractListModel;
58 import javax.swing.DefaultListCellRenderer;
59 import javax.swing.JCheckBox;
60 import javax.swing.JComboBox;
61 import javax.swing.JComponent;
62 import javax.swing.JLabel;
63 import javax.swing.JList;
64 import javax.swing.JPanel;
65 import javax.swing.JScrollPane;
66 import javax.swing.ListCellRenderer;
67 import javax.swing.ListSelectionModel;
68 import javax.swing.ScrollPaneConstants;
69 import javax.swing.TransferHandler;
70 import javax.swing.event.ListSelectionEvent;
71 import javax.swing.event.ListSelectionListener;
72
73 import com.cosylab.gui.components.NumberField;
74 import com.cosylab.gui.components.util.IconHelper;
75 import com.cosylab.gui.components.util.RunnerHelper;
76 import com.cosylab.gui.util.DisplayerParametersSelectorDialog;
77 import com.cosylab.util.ListenerList;
78
79 import de.desy.acop.displayers.tools.ConnectionParametersReceiver;
80 import de.desy.acop.displayers.tools.MultipleAcopDisplayer;
81 import de.desy.acop.transport.AccessMode;
82 import de.desy.acop.transport.ConnectionParameters;
83
84
85
86
87
88
89
90
91
92
93 public class SelectorListGUI extends JPanel implements Customizer {
94
95 private static final long serialVersionUID = -5435798671352845158L;
96
97 private static class AcopListModel extends AbstractListModel implements ListSelectionModel {
98 private static final long serialVersionUID = 399329824023675134L;
99 protected String[] elements= new String[0];
100 protected int selectedIndex = -1;
101 private ListenerList selectionListeners = new ListenerList(ListSelectionListener.class);
102
103
104
105
106 public AcopListModel() {
107
108 }
109
110
111 public int getSize() {
112 return elements.length;
113 }
114
115
116 public Object getElementAt(int index) {
117 if ( index >= 0 && index < elements.length)
118 return elements[index];
119 else
120 return null;
121 }
122
123 public void setElements(String[] obj) {
124 if (obj==elements || (obj==null && elements==null)) {
125 return;
126 }
127 removeAllElements();
128 if (obj != null) {
129 elements=obj;
130 fireIntervalAdded(this,0, obj.length-1);
131 if (obj.length > 0) {
132
133 }
134 }
135 }
136
137 public void reinstateLeadSelectedIndex() {
138 if (selectedIndex < 0) {
139 setLeadSelectionIndex(0);
140 }
141 }
142
143 public String[] getElements() {
144 return elements;
145 }
146
147
148
149
150 protected void removeAllElements() {
151 if ( elements.length > 0 ) {
152 int firstIndex = 0;
153 int lastIndex = elements.length - 1;
154 elements=new String[0];
155 fireIntervalRemoved(this, firstIndex, lastIndex);
156 }
157 }
158
159 public int getLeadSelectionIndex() {
160 return selectedIndex;
161 }
162
163 public void setLeadSelectionIndex(int index) {
164 if (index == selectedIndex) return;
165 int lower = Math.min(selectedIndex, index);
166 int higher = Math.max(selectedIndex, index);
167 selectedIndex = index;
168 fireSelectionChanged(lower, higher);
169 }
170
171 public void setSelectedValue(Object o) {
172 for (int i = 0; i < elements.length; i++) {
173 if (elements[i].equals(o)) {
174 setLeadSelectionIndex(i);
175 return;
176 }
177 }
178 }
179
180 public void clearSelection() {
181 setLeadSelectionIndex(-1);
182 }
183
184 public boolean isSelectionEmpty() {
185 return selectedIndex < 0;
186 }
187
188 public void addSelectionInterval(int index0, int index1) {
189 setLeadSelectionIndex(index0);
190 }
191
192 public int getAnchorSelectionIndex() {
193 return selectedIndex;
194 }
195
196 public int getMaxSelectionIndex() {
197 return selectedIndex;
198 }
199
200 public int getMinSelectionIndex() {
201 return selectedIndex;
202 }
203
204 public int getSelectionMode() {
205 return SINGLE_SELECTION;
206
207 }
208
209 public boolean getValueIsAdjusting() {
210 return false;
211 }
212
213 public void insertIndexInterval(int index, int length, boolean before) {
214
215 }
216
217 public boolean isSelectedIndex(int index) {
218 return index == selectedIndex;
219 }
220
221 public void removeIndexInterval(int index0, int index1) {
222 setLeadSelectionIndex(-1);
223 }
224
225 public void removeSelectionInterval(int index0, int index1) {
226 setLeadSelectionIndex(-1);
227 }
228
229 public void setAnchorSelectionIndex(int index) {
230 setLeadSelectionIndex(index);
231 }
232
233 public void setSelectionInterval(int index0, int index1) {
234 setLeadSelectionIndex(index0);
235 }
236
237 public void setSelectionMode(int selectionMode) {
238
239 }
240
241 public void setValueIsAdjusting(boolean valueIsAdjusting) {
242
243 }
244
245 public void addListSelectionListener(ListSelectionListener x) {
246 selectionListeners.add(x);
247 }
248
249 public void removeListSelectionListener(ListSelectionListener x) {
250 selectionListeners.remove(x);
251 }
252
253 protected void fireSelectionChanged(int firstIndex, int lastIndex) {
254 if (firstIndex < 0) firstIndex = 0;
255 if (lastIndex < 0) lastIndex = 0;
256 ListSelectionListener[] listeners = (ListSelectionListener[]) selectionListeners.toArray();
257 ListSelectionEvent e = new ListSelectionEvent(this, firstIndex, lastIndex, false);
258
259 for (ListSelectionListener l : listeners) {
260 l.valueChanged(e);
261 }
262
263 }
264
265 }
266
267 private Object disp;
268 private boolean initialized = false;
269
270 private JComboBox accessProtocolCombo;
271 private JList deviceContextList;
272 private JList subSystemList;
273 private JList deviceServerList;
274 private JList deviceNameList;
275 private JList propertyNameList;
276 private JScrollPane deviceContextScroll;
277 private JScrollPane subSystemScroll;
278 private JScrollPane deviceServerScroll;
279 private JScrollPane deviceNameScroll;
280 private JScrollPane propertyNameScroll;
281 private JPanel listPanel;
282 private JScrollPane listPanelScroll;
283 private AcopComboBoxModel<String> accessProtocolModel;
284 private AcopListModel deviceContextModel;
285 private AcopListModel subSystemModel;
286 private AcopListModel deviceServerModel;
287 private AcopListModel deviceNameModel;
288 private AcopListModel propertyNameModel;
289 private NumberField accessRateField;
290 private JLabel accessProtocolLabel;
291 private JLabel deviceContextLabel;
292 private JLabel subSystemLabel;
293 private JLabel deviceServerLabel;
294 private JLabel deviceNameLabel;
295 private JLabel propertyNameLabel;
296 private JLabel accessRateLabel;
297 private JLabel selectionLabel;
298 private JLabel descriptionLabel;
299 private JLabel accessModeLabel;
300 private JComboBox accessModeCombo;
301 private JCheckBox showStockProperties;
302
303 private ConnectionParameters connectionParameters;
304 private volatile Selector selector;
305
306 private ListenerList selectorGUIListeners = new ListenerList(SelectorGUIListener.class);
307
308 private PropertyChangeListener selectorPropertyListener;
309 private QueryListener selectorQueryListener;
310 private boolean settingFromSelector = false;
311
312 private Object selectedContext;
313 private Object selectedServer;
314 private Object selectedDevice;
315 private Object selectedProperty;
316
317
318
319
320
321 public SelectorListGUI() {
322 super();
323 initialize();
324 }
325
326 private void initialize() {
327 accessProtocolModel= new AcopComboBoxModel<String>(String.class);
328 deviceContextModel= new AcopListModel();
329 subSystemModel = new AcopListModel();
330 deviceServerModel= new AcopListModel();
331 deviceNameModel= new AcopListModel();
332 propertyNameModel = new AcopListModel();
333 setSelector(new Selector());
334
335 setLayout(new GridBagLayout());
336 setFocusable(false);
337 accessProtocolLabel = new JLabel("Access protocol");
338 accessProtocolLabel.setPreferredSize(new Dimension(120,21));
339 accessProtocolLabel.setMinimumSize(new Dimension(120,21));
340 accessProtocolLabel.setMaximumSize(new Dimension(120,21));
341 accessProtocolLabel.setFocusable(false);
342 add(accessProtocolLabel, new GridBagConstraints(0,0,1,1,0,0,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0,3,1,1), 0,0));
343 add(getAccessProtocolCombo(), new GridBagConstraints(1,0,1,1,0,0,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0,1,1,1), 0,0));
344 add(getShowStockPropertiesCheckBox(), new GridBagConstraints(2,0,1,1,0,0,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0,1,1,1), 0,0));
345
346 add(getListPanelScroll(), new GridBagConstraints(0,1,3,1,1,1,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0,1,1,1), 0,0));
347
348 accessRateLabel = new JLabel("Access rate");
349 accessRateLabel.setPreferredSize(new Dimension(120,21));
350 accessRateLabel.setMinimumSize(new Dimension(120,21));
351 accessRateLabel.setMaximumSize(new Dimension(120,21));
352 accessRateLabel.setFocusable(false);
353 add(accessRateLabel, new GridBagConstraints(0,4,1,1,0,0,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0,3,1,1), 0,0));
354 add(getAccessRateField(), new GridBagConstraints(1,4,1,1,0,0,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0,1,1,1), 0,0));
355
356 accessModeLabel = new JLabel("Access mode");
357 accessModeLabel.setPreferredSize(new Dimension(120,21));
358 accessModeLabel.setMinimumSize(new Dimension(120,21));
359 accessModeLabel.setMaximumSize(new Dimension(120,21));
360 accessModeLabel.setFocusable(false);
361 add(accessModeLabel, new GridBagConstraints(0,5,1,1,0,0,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0,3,1,1), 0,0));
362 add(getAccessModeCombo(), new GridBagConstraints(1,5,1,1,0,0,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0,1,1,1), 0,0));
363
364 add(getSelectionLabel(), new GridBagConstraints(0,6,3,1,0,0,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0,3,1,1),0,0));
365
366 if (!Beans.isDesignTime()) {
367 add(getDescriptionLabel(), new GridBagConstraints(0,7,3,1,0,0,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0,3,1,1),0,0));
368 }
369
370 setCombosEnabled(true);
371
372
373 initialized = true;
374 }
375
376 private void applySettings() {
377
378 try {
379 String remoteName = getSelector().getRemoteName();
380 ConnectionParameters old = connectionParameters;
381 int accessRate = getAccessRateField().getValue().intValue();
382 connectionParameters = new ConnectionParameters(remoteName,AccessMode.POLL, accessRate);
383 firePropertyChange(ConnectionParametersReceiver.CONNECTION_PARAMETERS_PROPERTY,old,getConnectionParameters());
384 getListPanel().revalidate();
385 } catch (Exception e) {
386 e.printStackTrace();
387 }
388 }
389
390 private JPanel getListPanel() {
391 if (listPanel == null) {
392 listPanel = new JPanel(new GridBagLayout());
393 listPanel.setFocusCycleRoot(true);
394
395 JPanel labelPanel = new JPanel(new GridLayout(1,5));
396 JPanel lists = new JPanel(new GridLayout(1,5));
397 deviceContextLabel = new JLabel("Device context");
398 deviceContextLabel.setPreferredSize(new Dimension(120,21));
399 deviceContextLabel.setMinimumSize(new Dimension(120,21));
400 deviceContextLabel.setMaximumSize(new Dimension(120,21));
401 deviceContextLabel.setFocusable(false);
402 labelPanel.add(deviceContextLabel);
403 deviceContextScroll = new JScrollPane(getDeviceContextList());
404 deviceContextScroll.setFocusable(false);
405 lists.add(deviceContextScroll);
406
407 subSystemLabel = new JLabel("Device subsystem");
408 subSystemLabel.setPreferredSize(new Dimension(120,21));
409 subSystemLabel.setMinimumSize(new Dimension(120,21));
410 subSystemLabel.setMaximumSize(new Dimension(120,21));
411 subSystemLabel.setFocusable(false);
412 labelPanel.add(subSystemLabel);
413 subSystemScroll = new JScrollPane(getSubSystemList());
414 subSystemScroll.setFocusable(false);
415 lists.add(subSystemScroll);
416
417 deviceServerLabel = new JLabel("Device server");
418 deviceServerLabel.setPreferredSize(new Dimension(120,21));
419 deviceServerLabel.setMinimumSize(new Dimension(120,21));
420 deviceServerLabel.setMaximumSize(new Dimension(120,21));
421 deviceServerLabel.setFocusable(false);
422 labelPanel.add(deviceServerLabel);
423 deviceServerScroll = new JScrollPane(getDeviceServerList());
424 deviceServerScroll.setFocusable(false);
425 lists.add(deviceServerScroll);
426
427 deviceNameLabel = new JLabel("Device name");
428 deviceNameLabel.setPreferredSize(new Dimension(120,21));
429 deviceNameLabel.setMinimumSize(new Dimension(120,21));
430 deviceNameLabel.setMaximumSize(new Dimension(120,21));
431 deviceNameLabel.setFocusable(false);
432 labelPanel.add(deviceNameLabel);
433 deviceNameScroll = new JScrollPane(getDeviceNameList());
434 deviceNameScroll.setFocusable(false);
435 lists.add(deviceNameScroll);
436
437 propertyNameLabel = new JLabel("Property name");
438 propertyNameLabel.setPreferredSize(new Dimension(120,21));
439 propertyNameLabel.setMinimumSize(new Dimension(120,21));
440 propertyNameLabel.setMaximumSize(new Dimension(120,21));
441 propertyNameLabel.setFocusable(false);
442 labelPanel.add(propertyNameLabel);
443 propertyNameScroll = new JScrollPane(getPropertyNameList());
444 propertyNameScroll.setFocusable(false);
445 lists.add(propertyNameScroll);
446
447 listPanel.add(labelPanel, new GridBagConstraints(0,0,1,1,1,0,GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(0,0,0,0),1,1));
448 listPanel.add(lists, new GridBagConstraints(0,1,1,1,1,1,GridBagConstraints.NORTH, GridBagConstraints.BOTH, new Insets(0,0,0,0),1,1));
449
450 }
451
452 listPanel.setMaximumSize(new Dimension(765,100));
453 listPanel.setMinimumSize(new Dimension(765,100));
454 listPanel.setPreferredSize(new Dimension(765,100));
455 return listPanel;
456 }
457
458 private JComboBox getAccessModeCombo() {
459 if (accessModeCombo == null) {
460 accessModeCombo = new JComboBox(AccessMode.values());
461 accessModeCombo.removeItem(AccessMode.WRITE);
462 accessModeCombo.removeItem(AccessMode.WRITE_CONNECT);
463 accessModeCombo.setSelectedItem(AccessMode.POLL);
464 accessModeCombo.setPreferredSize(new Dimension(120, 21));
465 accessModeCombo.setMinimumSize(new Dimension(120, 21));
466 accessModeCombo.setMaximumSize(new Dimension(120, 21));
467 accessModeCombo.addActionListener(new ActionListener(){
468 public void actionPerformed(ActionEvent e) {
469 if (settingFromSelector) return;
470 refreshSelectionLabel();
471 getSelector().setAccessMode((AccessMode)accessModeCombo.getSelectedItem());
472 }
473 });
474 }
475 return accessModeCombo;
476 }
477
478 private JScrollPane getListPanelScroll() {
479 if (listPanelScroll == null) {
480 listPanelScroll = new JScrollPane(getListPanel());
481 listPanelScroll.setFocusable(false);
482 listPanelScroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
483 }
484
485 return listPanelScroll;
486 }
487
488 private JLabel getSelectionLabel() {
489 if (selectionLabel == null) {
490 selectionLabel = new JLabel();
491 selectionLabel.setFocusable(false);
492 selectionLabel.setIcon(IconHelper.createIcon("icons/development/ApplicationDeploy16.gif"));
493 selectionLabel.addMouseMotionListener(new MouseMotionAdapter() {
494
495 @Override
496 public void mouseDragged(MouseEvent e) {
497 applySettings();
498 selectionLabel.getTransferHandler().exportAsDrag(selectionLabel,e,TransferHandler.COPY);
499 }
500
501 });
502
503 selectionLabel.setTransferHandler(new TransferHandler() {
504
505 private static final long serialVersionUID = 1658117801619157639L;
506 private DisplayerParametersSelectorDialog dialog;
507 @Override
508 public boolean importData(JComponent comp, Transferable t) {
509 try {
510 if (t.isDataFlavorSupported(DataFlavor.stringFlavor)) {
511 if (getConnectionParameters() == null) return false;
512 String input = t.getTransferData(DataFlavor.stringFlavor).toString();
513 StringTokenizer st = new StringTokenizer(input, "\n");
514 ArrayList<ConnectionParameters> al = new ArrayList<ConnectionParameters>(0);
515 while (st.hasMoreTokens()) {
516 al.add(new ConnectionParameters(
517 st.nextToken(), AccessMode.POLL,
518 1000));
519 }
520 ConnectionParameters[] cp = al.toArray(new ConnectionParameters[]{});
521 int idx = selectByDialog(getSelectionLabel(), cp);
522 if (idx < 0) return false;
523 setConnectionParameters(cp[idx]);
524 return true;
525 }
526 } catch (Exception e) {
527 e.printStackTrace();
528 }
529 return false;
530 }
531
532 @Override
533 protected Transferable createTransferable(JComponent c) {
534 return new Transferable() {
535
536 public Object getTransferData(DataFlavor flavor)
537 throws UnsupportedFlavorException, IOException {
538 if (flavor.isFlavorTextType()) {
539 return getConnectionParameters().getRemoteName();
540 }
541 throw new UnsupportedFlavorException(flavor);
542 }
543
544 public boolean isDataFlavorSupported(DataFlavor flavor) {
545 return flavor.isFlavorTextType();
546 }
547
548 public DataFlavor[] getTransferDataFlavors() {
549 return new DataFlavor[]{DataFlavor.stringFlavor};
550 }
551 };
552 }
553
554 @Override
555 public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) {
556 for (int i = 0; i < transferFlavors.length; i++) {
557 if (transferFlavors[i].isFlavorTextType()) {
558 return true;
559 }
560 }
561 return false;
562 }
563 @Override
564 public int getSourceActions(JComponent c) {
565 return TransferHandler.COPY;
566 }
567
568 protected int selectByDialog(Component c, ConnectionParameters[] dp) {
569 if (dialog == null) {
570 dialog = new DisplayerParametersSelectorDialog(c, "Adding Source", "Select Source:");
571 }
572 String[] s= new String[dp.length];
573 for (int i = 0; i < s.length; i++) {
574 s[i]=dp[i].getRemoteName();
575 }
576 return dialog.showSelectionDialog(c,s);
577 }
578 });
579
580 refreshSelectionLabel();
581 }
582 return selectionLabel;
583 }
584
585 private JLabel getDescriptionLabel() {
586 if (descriptionLabel == null) {
587 descriptionLabel = new JLabel();
588 descriptionLabel.setFocusable(false);
589 }
590
591 return descriptionLabel;
592 }
593
594 private JComboBox getAccessProtocolCombo() {
595 if (accessProtocolCombo == null) {
596 accessProtocolCombo = new JComboBox();
597 accessProtocolCombo.setModel(accessProtocolModel);
598 accessProtocolModel.setElements(new String[]{Selector.PROTOCOL_TINE});
599 accessProtocolModel.setSelectedItem(null);
600 accessProtocolCombo.setPreferredSize(new Dimension(120, 21));
601 accessProtocolCombo.setMinimumSize(new Dimension(120, 21));
602 accessProtocolCombo.setMaximumSize(new Dimension(120, 21));
603 accessProtocolCombo.addActionListener(new ActionListener(){
604 public void actionPerformed(ActionEvent e) {
605 if (settingFromSelector) return;
606 getExecutorService().execute(new Runnable(){
607 public void run() {
608 getSelector().setSelectedProtocol(accessProtocolModel.getSelectedItem());
609 getDeviceContextList().requestFocus();
610 }
611 });
612 }
613 });
614 accessProtocolCombo.addKeyListener(new KeyAdapter() {
615 @Override
616 public void keyPressed(KeyEvent e1) {
617 final KeyEvent e = e1;
618 getExecutorService().execute(new Runnable(){
619 public void run() {
620 if (e.getKeyCode() == KeyEvent.VK_RIGHT || e.getKeyCode() == KeyEvent.VK_ENTER) {
621 getSelector().setSelectedContext((String) deviceContextList.getSelectedValue());
622 getDeviceContextList().requestFocus();
623 } else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
624 getSelector().setSelectedProtocol(null);
625 getAccessProtocolCombo().requestFocus();
626 }
627 }
628 });
629 }
630 });
631 }
632 return accessProtocolCombo;
633 }
634
635 private JList getDeviceContextList() {
636 if (deviceContextList == null) {
637 deviceContextList = new JList();
638 deviceContextList.setModel(deviceContextModel);
639 deviceContextList.setCellRenderer(new CellRenderer("context"));
640 deviceContextList.setSelectionModel(deviceContextModel);
641 deviceContextList.addKeyListener(new KeyAdapter() {
642 @Override
643 public void keyPressed(KeyEvent e1) {
644 final KeyEvent e = e1;
645 getExecutorService().execute(new Runnable(){
646 public void run() {
647 if (e.getKeyCode() == KeyEvent.VK_RIGHT || e.getKeyCode() == KeyEvent.VK_ENTER) {
648 getSelector().setSelectedContext((String) deviceContextList.getSelectedValue());
649 getDeviceServerList().requestFocus();
650 }
651
652 }
653 });
654 }
655 });
656
657 deviceContextList.addMouseListener(new MouseAdapter() {
658 public void mousePressed(MouseEvent e) {
659 getExecutorService().execute(new Runnable(){
660 public void run() {
661 getSelector().setSelectedContext((String) deviceContextList.getSelectedValue());
662 }
663 });
664 }
665 });
666 deviceContextList.addFocusListener(new FocusAdapter(){
667 @Override
668 public void focusGained(FocusEvent e) {
669 if (getSelector().getSelectedContext() == null && deviceContextModel.getSize() > 0) {
670 deviceContextList.setSelectedValue(selectedContext, true);
671 deviceContextModel.reinstateLeadSelectedIndex();
672 }
673 }
674 });
675 }
676 return deviceContextList;
677 }
678
679 private JList getSubSystemList() {
680 if (subSystemList == null) {
681 subSystemList = new JList();
682
683
684
685
686 subSystemList.setModel(subSystemModel);
687 subSystemList.setCellRenderer(new CellRenderer("subSystem"));
688 subSystemList.setSelectionModel(subSystemModel);
689 subSystemList.setFocusable(false);
690 subSystemList.addKeyListener(new KeyAdapter() {
691 @Override
692 public void keyPressed(KeyEvent e1) {
693 final KeyEvent e = e1;
694 getExecutorService().execute(new Runnable(){
695 public void run() {
696 if (e.getKeyCode() == KeyEvent.VK_RIGHT || e.getKeyCode() == KeyEvent.VK_ENTER) {
697 getSelector().setSelectedSubSystem((String) subSystemList.getSelectedValue());
698 getDeviceServerList().requestFocus();
699 } else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
700 selectedContext = getDeviceContextList().getSelectedValue();
701 getSelector().setSelectedContext(null);
702 getDeviceContextList().requestFocus();
703 }
704
705 }
706 });
707 }
708 });
709 subSystemList.addMouseListener(new MouseAdapter() {
710 public void mousePressed(MouseEvent e) {
711 getExecutorService().execute(new Runnable(){
712 public void run() {
713 getSelector().setSelectedSubSystem((String) subSystemList.getSelectedValue());
714 }
715 });
716 }
717 });
718 subSystemList.addFocusListener(new FocusAdapter(){
719 @Override
720 public void focusGained(FocusEvent e) {
721 if (getSelector().getSelectedSubSystem() == null && subSystemModel.getSize() > 0) {
722 subSystemList.setSelectedIndex(0);
723 }
724 }
725 });
726 }
727 return subSystemList;
728 }
729
730 private JList getDeviceServerList() {
731 if (deviceServerList == null) {
732 deviceServerList = new JList();
733 deviceServerList.setModel(deviceServerModel);
734 deviceServerList.setCellRenderer(new CellRenderer("server"));
735 deviceServerList.setSelectionModel(deviceServerModel);
736 deviceServerList.addKeyListener(new KeyAdapter() {
737 @Override
738 public void keyPressed(KeyEvent e1) {
739 final KeyEvent e = e1;
740 getExecutorService().execute(new Runnable(){
741 public void run() {
742 if (e.getKeyCode() == KeyEvent.VK_RIGHT || e.getKeyCode() == KeyEvent.VK_ENTER) {
743 getSelector().setSelectedServer((String) deviceServerList.getSelectedValue());
744 getDeviceNameList().requestFocus();
745 } else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
746 selectedContext = getDeviceContextList().getSelectedValue();
747 getSelector().setSelectedContext(null);
748 getDeviceContextList().requestFocus();
749 }
750 }
751 });
752 }
753 });
754 deviceServerList.addMouseListener(new MouseAdapter() {
755 public void mousePressed(MouseEvent e) {
756 getExecutorService().execute(new Runnable(){
757 public void run() {
758 getSelector().setSelectedServer((String) deviceServerList.getSelectedValue());
759 }
760 });
761 }
762 });
763 deviceServerList.addFocusListener(new FocusAdapter(){
764 @Override
765 public void focusGained(FocusEvent e) {
766 if (deviceServerModel.getSize() < 0 || getSelector().getSelectedContext() == null) {
767 getDeviceServerList().requestFocus();
768 } else if (getSelector().getSelectedServer() == null && deviceServerModel.getSize() > 0) {
769 deviceServerList.setSelectedValue(selectedServer, true);
770 deviceServerModel.reinstateLeadSelectedIndex();
771 }
772 }
773 });
774 }
775 return deviceServerList;
776 }
777
778 private JList getDeviceNameList() {
779 if (deviceNameList == null) {
780 deviceNameList = new JList();
781 deviceNameList.setModel(deviceNameModel);
782 deviceNameList.setCellRenderer(new CellRenderer("device"));
783 deviceNameList.setSelectionModel(deviceNameModel);
784 deviceNameList.addKeyListener(new KeyAdapter() {
785 @Override
786 public void keyPressed(KeyEvent e1) {
787 final KeyEvent e = e1;
788 getExecutorService().execute(new Runnable(){
789 public void run() {
790 if (e.getKeyCode() == KeyEvent.VK_RIGHT || e.getKeyCode() == KeyEvent.VK_ENTER) {
791 getSelector().setSelectedDevice((String) deviceNameList.getSelectedValue());
792 getPropertyNameList().requestFocus();
793 } else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
794 selectedServer = getDeviceServerList().getSelectedValue();
795 getSelector().setSelectedServer(null);
796 getDeviceServerList().requestFocus();
797 }
798 }
799 });
800 }
801 });
802 deviceNameList.addMouseListener(new MouseAdapter() {
803 public void mousePressed(MouseEvent e) {
804 getExecutorService().execute(new Runnable(){
805 public void run() {
806 getSelector().setSelectedDevice((String) deviceNameList.getSelectedValue());
807 }
808 });
809 }
810 });
811
812 deviceNameList.addFocusListener(new FocusAdapter(){
813 @Override
814 public void focusGained(FocusEvent e) {
815 if (deviceNameModel.getSize() < 0 || getSelector().getSelectedServer() == null) {
816 getDeviceServerList().requestFocus();
817 } else if (getSelector().getSelectedDevice() == null && deviceNameModel.getSize() > 0) {
818 deviceNameList.setSelectedValue(selectedDevice, true);
819 deviceNameModel.reinstateLeadSelectedIndex();
820 }
821 }
822 });
823 }
824 return deviceNameList;
825 }
826
827 private JList getPropertyNameList() {
828 if (propertyNameList == null) {
829 propertyNameList = new JList();
830 propertyNameList.setModel(propertyNameModel);
831 propertyNameList.setCellRenderer(new CellRenderer("property"));
832 propertyNameList.setSelectionModel(propertyNameModel);
833 propertyNameModel.addListSelectionListener(new ListSelectionListener(){
834 public void valueChanged(ListSelectionEvent e) {
835 selectedProperty = propertyNameList.getSelectedValue();
836 }
837 });
838 propertyNameList.addKeyListener(new KeyAdapter() {
839 @Override
840 public void keyPressed(KeyEvent e1) {
841 final KeyEvent e = e1;
842 getExecutorService().execute(new Runnable(){
843 public void run() {
844 if (e.getKeyCode() == KeyEvent.VK_RIGHT || e.getKeyCode() == KeyEvent.VK_ENTER) {
845 getSelector().setSelectedProperty((String) propertyNameList.getSelectedValue());
846 fireSelectorGUIEvent(true);
847 } else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
848 selectedDevice = getDeviceNameList().getSelectedValue();
849 getSelector().setSelectedProperty(null);
850 getSelector().setSelectedDevice(null);
851 getDeviceNameList().requestFocus();
852 }
853 }
854 });
855 }
856 });
857
858 propertyNameList.addMouseListener(new MouseAdapter() {
859 public void mousePressed(MouseEvent e) {
860 getExecutorService().execute(new Runnable(){
861 public void run() {
862 getSelector().setSelectedProperty((String) propertyNameList.getSelectedValue());
863 }
864 });
865 }
866 });
867
868 propertyNameList.addFocusListener(new FocusAdapter(){
869 @Override
870 public void focusGained(FocusEvent e) {
871 if (getSelector().getSelectedProperty() == null && propertyNameModel.getSize() > 0) {
872 propertyNameList.setSelectedValue(selectedProperty, true);
873 propertyNameModel.reinstateLeadSelectedIndex();
874 }
875 if (getSelector().getSelectedDevice() == null && getSelector().getSelectedProperty() == null) {
876 getDeviceNameList().requestFocus();
877 }
878 }
879 });
880 }
881 return propertyNameList;
882 }
883
884 private JCheckBox getShowStockPropertiesCheckBox() {
885 if (showStockProperties == null) {
886 showStockProperties = new JCheckBox("Show stock properties");
887 showStockProperties.addActionListener(new ActionListener() {
888 public void actionPerformed(ActionEvent e) {
889 getExecutorService().execute(new Runnable() {
890 public void run() {
891 getSelector().setShowStockProperties(showStockProperties.isSelected());
892 }
893 });
894 }
895 });
896 }
897 return showStockProperties;
898 }
899
900 private NumberField getAccessRateField() {
901 if (accessRateField == null) {
902 accessRateField = new NumberField();
903 accessRateField.setNumberType(Integer.class);
904 accessRateField.setFormat("%d");
905 accessRateField.setPreferredSize(new Dimension(121, 21));
906 accessRateField.setMinimumSize(new Dimension(121, 21));
907 accessRateField.setMaximumSize(new Dimension(121, 21));
908 accessRateField.setText(String.valueOf(1000));
909 accessRateField.addPropertyChangeListener("value", new PropertyChangeListener() {
910 public void propertyChange(PropertyChangeEvent evt) {
911 getExecutorService().execute(new Runnable(){
912 public void run() {
913 refreshSelectionLabel();
914 getSelector().setAccessRate(accessRateField.getValue().intValue());
915 }
916 });
917 }
918 });
919 }
920 return accessRateField;
921 }
922
923 private void refreshSelectionLabel() {
924 applySettings();
925 String text = connectionParameters.toString();
926 getSelectionLabel().setText(text);
927 getSelectionLabel().setToolTipText(text);
928 }
929
930 private void update(final ConnectionParameters params) {
931 if (params == null || params.equals(connectionParameters)) return;
932 ConnectionParameters old = connectionParameters;
933 connectionParameters = params;
934 getExecutorService().execute(new Runnable() {
935 public void run() {
936 getSelector().setSelectedProtocol(params.getAccessProtocol());
937 getSelector().setSelectedContext(params.getDeviceContext());
938 getSelector().setSelectedServer(params.getDeviceGroup());
939 getSelector().setSelectedDevice(params.getDeviceName());
940 getSelector().setSelectedProperty(params.getDeviceProperty());
941 getSelector().setSelectedDevice(params.getDeviceName());
942 }
943 });
944 getSelector().setAccessRate(params.getAccessRate());
945 firePropertyChange(ConnectionParametersReceiver.CONNECTION_PARAMETERS_PROPERTY,old,connectionParameters);
946 }
947
948
949
950
951
952 public void setObject(Object bean) {
953 if (bean instanceof MultipleAcopDisplayer) {
954 disp = bean;
955 } else if (bean instanceof ConnectionParametersReceiver) {
956 disp = bean;
957 update(((ConnectionParametersReceiver)disp).getConnectionParameters());
958 ((ConnectionParametersReceiver)disp).addPropertyChangeListener(ConnectionParametersReceiver.CONNECTION_PARAMETERS_PROPERTY, new PropertyChangeListener(){
959
960 public void propertyChange(PropertyChangeEvent evt) {
961 if (!ConnectionParametersReceiver.CONNECTION_PARAMETERS_PROPERTY.equals(evt.getPropertyName())) return;
962 update(((ConnectionParametersReceiver)disp).getConnectionParameters());
963 }
964
965 });
966 } else {
967 throw new IllegalArgumentException("Only ConnectionParametersReceiver and" +
968 " MultipleAcopDisplayer can use this Customizer.");
969 }
970
971
972 }
973
974
975
976
977
978
979 public ConnectionParameters getConnectionParameters() {
980 return connectionParameters;
981 }
982
983
984
985
986
987
988 public void setConnectionParameters(ConnectionParameters parameters) {
989 if (this.connectionParameters != null && this.connectionParameters.equals(parameters)) return;
990 update(parameters);
991 }
992
993 private PropertyChangeListener getSelectorPropertyListener() {
994 if (selectorPropertyListener == null) {
995 selectorPropertyListener = new PropertyChangeListener(){
996 public void propertyChange(final PropertyChangeEvent evt) {
997 if (getSelector() == null) return;
998 if (!getPropertyNameList().isEnabled()) return;
999 String process = evt.getPropertyName();
1000 String name = evt.getPropertyName();
1001 if ("accessRate".equals(process)) {
1002 getAccessRateField().setValue(getSelector().getAccessRate());
1003 refreshSelectionLabel();
1004 getAccessRateField().requestFocus();
1005 return;
1006 } else if ("accessMode".equals(name)) {
1007 getAccessModeCombo().setSelectedItem(getSelector().getAccessMode());
1008 refreshSelectionLabel();
1009 getAccessModeCombo().requestFocus();
1010 return;
1011 }
1012
1013 if ("selectedProtocol".equals(name)) {
1014 accessProtocolModel.setSelectedItem(getSelector().getSelectedProtocol());
1015 } else if ("selectedContext".equals(name)) {
1016 deviceContextModel.setSelectedValue(getSelector().getSelectedContext());
1017 } else if ("selectedSubSystem".equals(name)) {
1018 subSystemModel.setSelectedValue(getSelector().getSelectedSubSystem());
1019 } else if ("selectedServer".equals(name)) {
1020 deviceServerModel.setSelectedValue(getSelector().getSelectedServer());
1021 } else if ("selectedDevice".equals(name)) {
1022 deviceNameModel.setSelectedValue(getSelector().getSelectedDevice());
1023 } else if ("selectedProperty".equals(name)) {
1024 propertyNameModel.setSelectedValue(getSelector().getSelectedProperty());
1025 } else if ("availableContexts".equals(name)) {
1026 deviceContextModel.setElements(getSelector().getAvailableContexts());
1027 } else if ("availableSubsystems".equals(name)) {
1028 subSystemModel.setElements(getSelector().getAvailableSubsystems());
1029 } else if ("availableServers".equals(name)) {
1030 deviceServerModel.setElements(getSelector().getAvailableServers());
1031 } else if ("availableDevices".equals(name)) {
1032 deviceNameModel.setElements(getSelector().getAvailableDevices());
1033 } else if ("availableProperties".equals(name)) {
1034 propertyNameModel.setElements(getSelector().getAvailableProperties());
1035 } else if ("showStockProperties".equals(name)) {
1036 getShowStockPropertiesCheckBox().setSelected(getSelector().isShowStockProperties());
1037 }
1038
1039 refreshSelectionLabel();
1040
1041 if (!Beans.isDesignTime()) {
1042 if ("selectedProperty".equals(evt.getPropertyName())) {
1043 if (evt.getNewValue() == null) {
1044 getDescriptionLabel().setText("");
1045 getDescriptionLabel().setToolTipText("");
1046 return;
1047 }
1048 if (evt.getNewValue() != evt.getOldValue()) {
1049 if (SelectorUtilities.isConnectionParametersValid(connectionParameters)) {
1050 String text = SelectorUtilities.getInfo(connectionParameters);
1051 getDescriptionLabel().setText(text);
1052 getDescriptionLabel().setToolTipText(text);
1053 }
1054 }
1055 }
1056 }
1057 }
1058 };
1059 }
1060 return selectorPropertyListener;
1061 }
1062
1063 private QueryListener getSelectorQueryListener() {
1064 if (selectorQueryListener == null) {
1065 selectorQueryListener = new QueryListener() {
1066 public void queryFinished(final QueryEvent e) {
1067 fillUpValues();
1068 adjustScrollBar(e.getProcess());
1069 refreshSelectionLabel();
1070 setCombosEnabled(true);
1071 }
1072 public void queryStarted(QueryEvent e) {
1073 setCombosEnabled(false);
1074 }
1075 };
1076 }
1077 return selectorQueryListener;
1078 }
1079
1080 protected void setSelector(Selector selector) {
1081 if (this.selector != null && this.selector.equals(selector)) return;
1082 if (this.selector != null) {
1083 this.selector.removePropertyChangeListener(getSelectorPropertyListener());
1084 this.selector.removeQueryListener(getSelectorQueryListener());
1085 }
1086 Selector old = this.selector;
1087 this.selector = selector;
1088 if (this.selector != null) {
1089 this.selector.addPropertyChangeListener(getSelectorPropertyListener());
1090 this.selector.addQueryListener(getSelectorQueryListener());
1091 }
1092 fillUpValues();
1093 refreshSelectionLabel();
1094 firePropertyChange("selector", old, this.selector);
1095 }
1096
1097 protected Selector getSelector() {
1098 return selector;
1099 }
1100
1101 private void adjustScrollBar(String process) {
1102 int width = 0;
1103 int extent = 0;
1104 if (initialized) {
1105 width = getListPanelScroll().getHorizontalScrollBar().getMaximum()/5;
1106 extent = getListPanelScroll().getHorizontalScrollBar().getModel().getExtent();
1107 }
1108
1109 if ("protocol".equals(process)) {
1110 getListPanelScroll().getHorizontalScrollBar().setValue(0);
1111 } else if ("context".equals(process)) {
1112 getListPanelScroll().getHorizontalScrollBar().setValue(2*width-extent);
1113 } else if ("subSystem".equals(process)) {
1114 getListPanelScroll().getHorizontalScrollBar().setValue(3*width-extent);
1115 } else if ("server".equals(process)) {
1116 getListPanelScroll().getHorizontalScrollBar().setValue(4*width-extent);
1117 } else if ("device".equals(process)) {
1118 getListPanelScroll().getHorizontalScrollBar().setValue(5*width-extent);
1119 } else if ("property".equals(process)) {
1120 getListPanelScroll().getHorizontalScrollBar().setValue(5*width);
1121 }
1122 }
1123
1124 private void fillUpValues() {
1125 settingFromSelector = true;
1126 deviceContextModel.setElements(getSelector().getAvailableContexts());
1127 subSystemModel.setElements(getSelector().getAvailableSubsystems());
1128 deviceServerModel.setElements(getSelector().getAvailableServers());
1129 deviceNameModel.setElements(getSelector().getAvailableDevices());
1130 propertyNameModel.setElements(getSelector().getAvailableProperties());
1131 setSelectedValues();
1132 settingFromSelector = false;
1133 }
1134
1135 private void setSelectedValues() {
1136 getAccessRateField().setValue(getSelector().getAccessRate());
1137 getAccessModeCombo().setSelectedItem(getSelector().getAccessMode());
1138 if (getSelector().getSelectedProtocol() != null) {
1139 if (!getSelector().getSelectedProtocol().equals(getAccessProtocolCombo().getSelectedItem()))
1140 getAccessProtocolCombo().setSelectedItem(getSelector().getSelectedProtocol());
1141 } else {
1142 if (getAccessProtocolCombo().getSelectedItem() != null)
1143 getAccessProtocolCombo().setSelectedItem(null);
1144 }
1145 getShowStockPropertiesCheckBox().setSelected(getSelector().isShowStockProperties());
1146 if (getSelector().getSelectedContext() != null) {
1147 if (!getSelector().getSelectedContext().equals(getDeviceContextList().getSelectedValue()))
1148 getDeviceContextList().setSelectedValue(getSelector().getSelectedContext(), true);
1149 } else {
1150 getDeviceContextList().setSelectedValue(selectedContext, true);
1151 deviceContextModel.reinstateLeadSelectedIndex();
1152 }
1153
1154 if (getSelector().getSelectedSubSystem() != null) {
1155 if (!getSelector().getSelectedSubSystem().equals(getSubSystemList().getSelectedValue()))
1156 getSubSystemList().setSelectedValue(getSelector().getSelectedSubSystem(), true);
1157 } else {
1158 getSubSystemList().setSelectedIndex(0);
1159 subSystemModel.reinstateLeadSelectedIndex();
1160 }
1161
1162 if (getSelector().getSelectedServer() != null) {
1163 if (!getSelector().getSelectedServer().equals(getDeviceServerList().getSelectedValue()))
1164 getDeviceServerList().setSelectedValue(getSelector().getSelectedServer(), true);
1165 } else {
1166 getDeviceServerList().setSelectedValue(selectedServer, true);
1167 deviceServerModel.reinstateLeadSelectedIndex();
1168 }
1169
1170 if (getSelector().getSelectedDevice() != null) {
1171 if (!getSelector().getSelectedDevice().equals(getDeviceNameList().getSelectedValue()))
1172 getDeviceNameList().setSelectedValue(getSelector().getSelectedDevice(), true);
1173 } else {
1174 getDeviceNameList().setSelectedValue(selectedDevice, true);
1175 deviceNameModel.reinstateLeadSelectedIndex();
1176 }
1177
1178 if (getSelector().getSelectedProperty() != null) {
1179 if (!getSelector().getSelectedProperty().equals(getPropertyNameList().getSelectedValue()))
1180 getPropertyNameList().setSelectedValue(getSelector().getSelectedProperty(), true);
1181 } else {
1182 getPropertyNameList().setSelectedValue(selectedProperty, true);
1183 propertyNameModel.reinstateLeadSelectedIndex();
1184 }
1185
1186 }
1187
1188 private void setCombosEnabled(boolean enabled) {
1189 getAccessProtocolCombo().setEnabled(enabled);
1190 getDeviceContextList().setEnabled(enabled);
1191 getSubSystemList().setEnabled(enabled);
1192 getDeviceServerList().setEnabled(enabled);
1193 getDeviceNameList().setEnabled(enabled);
1194 getPropertyNameList().setEnabled(enabled);
1195 }
1196
1197
1198
1199
1200
1201
1202 public void addSelectorGUIListener(SelectorGUIListener l) {
1203 selectorGUIListeners.add(l);
1204 }
1205
1206
1207
1208
1209
1210
1211 public void removeSelectorGUIListener(SelectorGUIListener l) {
1212 selectorGUIListeners.remove(l);
1213 }
1214
1215 protected void fireSelectorGUIEvent(boolean selectionComplete) {
1216 SelectorGUIListener[] listeners = (SelectorGUIListener[]) selectorGUIListeners.toArray();
1217 SelectorGUIEvent e = new SelectorGUIEvent(this, selectionComplete);
1218 for (SelectorGUIListener listener : listeners) {
1219 listener.selectionChanged(e);
1220 }
1221 }
1222
1223 public static void main(String[] args) {
1224 try {
1225
1226 SelectorListGUI s = new SelectorListGUI();
1227 RunnerHelper.runComponent(s,500,500);
1228
1229 } catch (Exception e) {
1230 e.printStackTrace();
1231 }
1232 }
1233
1234 private class CellRenderer extends DefaultListCellRenderer implements ListCellRenderer {
1235
1236 private static final long serialVersionUID = -1255512223908432074L;
1237
1238 private String process;
1239 public CellRenderer(String process) {
1240 super();
1241 this.process = process;
1242 }
1243
1244 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
1245 Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
1246 if (!(c instanceof JLabel)) return c;
1247 if (value == null) value = "";
1248 JLabel component = (JLabel)c;
1249 component.setText(" " + value.toString());
1250
1251 String selectedValue = null;
1252 if ("context".equals(process)) {
1253 selectedValue = getSelector().getSelectedContext();
1254 } else if ("subSystem".equals(process)) {
1255 selectedValue = getSelector().getSelectedSubSystem();
1256 } else if ("server".equals(process)) {
1257 selectedValue = getSelector().getSelectedServer();
1258 } else if ("device".equals(process)) {
1259 selectedValue = getSelector().getSelectedDevice();
1260 } else if ("property".equals(process)) {
1261 selectedValue = getSelector().getSelectedProperty();
1262 }
1263 if (value.equals(selectedValue)) {
1264 component.setFont(new Font(getFont().getName(), Font.BOLD, getFont().getSize()));
1265 } else {
1266 component.setFont(new Font(getFont().getName(), Font.PLAIN, getFont().getSize()));
1267 }
1268
1269 return component;
1270
1271 }
1272
1273 }
1274
1275 private ExecutorService executorService;
1276 private synchronized ExecutorService getExecutorService() {
1277 if (executorService == null) {
1278
1279 executorService = new ThreadPoolExecutor(1, 1, 0, TimeUnit.NANOSECONDS,
1280 new LinkedBlockingQueue<Runnable>()){
1281 @Override
1282 protected void afterExecute(Runnable r, Throwable t) {
1283 super.afterExecute(r, t);
1284 if (t != null) {
1285 t.printStackTrace();
1286 }
1287 }
1288 };
1289 ((ThreadPoolExecutor)executorService).setRejectedExecutionHandler(new RejectedExecutionHandler(){
1290 public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
1291
1292 }
1293 });
1294 }
1295 return executorService;
1296 }
1297
1298 synchronized void setExecutorService(ExecutorService service) {
1299 if (executorService != null) executorService.shutdown();
1300 this.executorService = service;
1301 }
1302
1303 }
1304