This project has retired. For details please refer to its Attic page.
PropertyEditorFrame xref

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.chemistry.opencmis.workbench;
20  
21  import java.awt.BorderLayout;
22  import java.awt.Color;
23  import java.awt.Cursor;
24  import java.awt.Dimension;
25  import java.awt.FlowLayout;
26  import java.awt.Font;
27  import java.awt.event.ActionEvent;
28  import java.awt.event.ActionListener;
29  import java.awt.event.KeyEvent;
30  import java.awt.event.KeyListener;
31  import java.math.BigDecimal;
32  import java.text.DecimalFormat;
33  import java.text.Format;
34  import java.util.ArrayList;
35  import java.util.Calendar;
36  import java.util.Collections;
37  import java.util.GregorianCalendar;
38  import java.util.HashMap;
39  import java.util.LinkedList;
40  import java.util.List;
41  import java.util.Map;
42  import java.util.TimeZone;
43  
44  import javax.swing.BorderFactory;
45  import javax.swing.BoxLayout;
46  import javax.swing.ImageIcon;
47  import javax.swing.JButton;
48  import javax.swing.JComboBox;
49  import javax.swing.JComponent;
50  import javax.swing.JFormattedTextField;
51  import javax.swing.JFrame;
52  import javax.swing.JLabel;
53  import javax.swing.JPanel;
54  import javax.swing.JScrollPane;
55  import javax.swing.JSpinner;
56  import javax.swing.JTextField;
57  import javax.swing.SpinnerListModel;
58  import javax.swing.SpinnerNumberModel;
59  import javax.swing.UIManager;
60  import javax.swing.event.ChangeEvent;
61  import javax.swing.event.ChangeListener;
62  
63  import org.apache.chemistry.opencmis.client.api.CmisObject;
64  import org.apache.chemistry.opencmis.client.api.ObjectId;
65  import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
66  import org.apache.chemistry.opencmis.commons.enums.Action;
67  import org.apache.chemistry.opencmis.commons.enums.Cardinality;
68  import org.apache.chemistry.opencmis.commons.enums.Updatability;
69  import org.apache.chemistry.opencmis.workbench.PropertyEditorFrame.UpdateStatus.StatusFlag;
70  import org.apache.chemistry.opencmis.workbench.model.ClientModel;
71  
72  /**
73   * Simple property editor.
74   */
75  public class PropertyEditorFrame extends JFrame {
76  
77      private static final long serialVersionUID = 1L;
78  
79      private static final String WINDOW_TITLE = "Property Editor";
80      private static final ImageIcon ICON_ADD = ClientHelper.getIcon("add.png");
81  
82      private final ClientModel model;
83      private final CmisObject object;
84      private List<PropertyInputPanel> propertyPanels;
85  
86      public PropertyEditorFrame(final ClientModel model, final CmisObject object) {
87          super();
88  
89          this.model = model;
90          this.object = object;
91  
92          createGUI();
93      }
94  
95      private void createGUI() {
96          setTitle(WINDOW_TITLE);
97          setPreferredSize(new Dimension(800, 600));
98          setMinimumSize(new Dimension(300, 120));
99  
100         setLayout(new BorderLayout());
101 
102         final JPanel panel = new JPanel();
103         panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
104 
105         final Font labelFont = UIManager.getFont("Label.font");
106         final Font boldFont = labelFont.deriveFont(Font.BOLD, labelFont.getSize2D() * 1.2f);
107 
108         final JPanel topPanel = new JPanel();
109         topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.Y_AXIS));
110         topPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
111         final JLabel nameLabel = new JLabel(object.getName());
112         nameLabel.setFont(boldFont);
113         topPanel.add(nameLabel);
114         topPanel.add(new JLabel(object.getId()));
115         add(topPanel, BorderLayout.PAGE_START);
116 
117         JScrollPane scrollPane = new JScrollPane(panel);
118         add(scrollPane, BorderLayout.CENTER);
119 
120         propertyPanels = new ArrayList<PropertyEditorFrame.PropertyInputPanel>();
121 
122         int position = 0;
123         for (PropertyDefinition<?> propDef : object.getType().getPropertyDefinitions().values()) {
124             boolean isUpdatable = (propDef.getUpdatability() == Updatability.READWRITE)
125                     || (propDef.getUpdatability() == Updatability.WHENCHECKEDOUT && object.getAllowableActions()
126                             .getAllowableActions().contains(Action.CAN_CHECK_IN));
127 
128             if (isUpdatable) {
129                 PropertyInputPanel propertyPanel = new PropertyInputPanel(propDef, object.getPropertyValue(propDef
130                         .getId()), position++);
131 
132                 propertyPanels.add(propertyPanel);
133                 panel.add(propertyPanel);
134             }
135         }
136 
137         JButton updateButton = new JButton("Update");
138         updateButton.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
139         updateButton.setDefaultCapable(true);
140         updateButton.addActionListener(new ActionListener() {
141             public void actionPerformed(ActionEvent event) {
142                 if (doUpdate()) {
143                     dispose();
144                 }
145             }
146         });
147 
148         add(updateButton, BorderLayout.PAGE_END);
149 
150         setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
151         pack();
152         setLocationRelativeTo(null);
153         setVisible(true);
154     }
155 
156     /**
157      * Performs the update.
158      */
159     private boolean doUpdate() {
160         try {
161             setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
162 
163             Map<String, Object> properties = new HashMap<String, Object>();
164             for (PropertyInputPanel propertyPanel : propertyPanels) {
165                 if (propertyPanel.includeInUpdate()) {
166                     properties.put(propertyPanel.getId(), propertyPanel.getValue());
167                 }
168             }
169 
170             if (properties.isEmpty()) {
171                 return false;
172             }
173 
174             ObjectId newId = object.updateProperties(properties, false);
175 
176             if ((newId != null) && newId.getId().equals(model.getCurrentObject().getId())) {
177                 try {
178                     model.reloadObject();
179                     model.reloadFolder();
180                 } catch (Exception ex) {
181                     ClientHelper.showError(null, ex);
182                 }
183             }
184 
185             return true;
186         } catch (Exception ex) {
187             ClientHelper.showError(this, ex);
188             return false;
189         } finally {
190             setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
191         }
192     }
193 
194     interface UpdateStatus {
195         enum StatusFlag {
196             DontChange, Update, Unset
197         }
198 
199         void setStatus(StatusFlag status);
200 
201         StatusFlag getStatus();
202     }
203 
204     interface MultivalueManager {
205         void addNewValue();
206 
207         void removeValue(int pos);
208 
209         void moveUp(int pos);
210 
211         void moveDown(int pos);
212     }
213 
214     /**
215      * Property input panel.
216      */
217     public static class PropertyInputPanel extends JPanel implements UpdateStatus, MultivalueManager {
218         private static final long serialVersionUID = 1L;
219 
220         private static final Color BACKGROUND1 = UIManager.getColor("Table:\"Table.cellRenderer\".background");
221         private static final Color BACKGROUND2 = UIManager.getColor("Table.alternateRowColor");
222         private static final Color LINE = new Color(0xB8, 0xB8, 0xB8);
223 
224         private final PropertyDefinition<?> propDef;
225         private final Object value;
226         private final Color bgColor;
227         private JComboBox changeBox;
228         private LinkedList<JComponent> valueComponents;
229 
230         public PropertyInputPanel(PropertyDefinition<?> propDef, Object value, int position) {
231             super();
232             this.propDef = propDef;
233             this.value = value;
234             bgColor = (position % 2 == 0 ? BACKGROUND1 : BACKGROUND2);
235             createGUI();
236         }
237 
238         private void createGUI() {
239             setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
240 
241             setBackground(bgColor);
242             setBorder(BorderFactory.createCompoundBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, LINE),
243                     BorderFactory.createEmptyBorder(10, 5, 10, 5)));
244 
245             Font labelFont = UIManager.getFont("Label.font");
246             Font boldFont = labelFont.deriveFont(Font.BOLD, labelFont.getSize2D() * 1.2f);
247 
248             JPanel titlePanel = new JPanel();
249             titlePanel.setLayout(new BorderLayout());
250             titlePanel.setBackground(bgColor);
251             titlePanel.setToolTipText("<html><b>" + propDef.getPropertyType().value() + "</b> ("
252                     + propDef.getCardinality().value() + " value)"
253                     + (propDef.getDescription() != null ? "<br>" + propDef.getDescription() : ""));
254             add(titlePanel);
255 
256             JPanel namePanel = new JPanel();
257             namePanel.setLayout(new BoxLayout(namePanel, BoxLayout.Y_AXIS));
258             namePanel.setBackground(bgColor);
259             JLabel displayNameLabel = new JLabel(propDef.getDisplayName());
260             displayNameLabel.setFont(boldFont);
261             namePanel.add(displayNameLabel);
262             JLabel idLabel = new JLabel(propDef.getId());
263             namePanel.add(idLabel);
264 
265             titlePanel.add(namePanel, BorderLayout.LINE_START);
266 
267             changeBox = new JComboBox(new Object[] { "Don't change     ", "Update    ", "Unset     " });
268             titlePanel.add(changeBox, BorderLayout.LINE_END);
269 
270             valueComponents = new LinkedList<JComponent>();
271             if (propDef.getCardinality() == Cardinality.SINGLE) {
272                 JComponent valueField = createInputField(value);
273                 valueComponents.add(valueField);
274                 add(valueField);
275             } else {
276                 if (value instanceof List<?>) {
277                     for (Object v : (List<?>) value) {
278                         JComponent valueField = new MultiValuePropertyInputField(createInputField(v), this, bgColor);
279                         valueComponents.add(valueField);
280                         add(valueField);
281                     }
282                 }
283 
284                 JPanel addPanel = new JPanel(new BorderLayout());
285                 addPanel.setBackground(bgColor);
286                 JButton addButton = new JButton(ICON_ADD);
287                 addButton.addActionListener(new ActionListener() {
288                     @Override
289                     public void actionPerformed(ActionEvent e) {
290                         addNewValue();
291                         setStatus(StatusFlag.Update);
292                     }
293                 });
294                 addPanel.add(addButton, BorderLayout.LINE_END);
295                 add(addPanel);
296 
297                 updatePositions();
298             }
299 
300             setMaximumSize(new Dimension(Short.MAX_VALUE, getPreferredSize().height));
301         }
302 
303         protected JComponent createInputField(Object value) {
304             switch (propDef.getPropertyType()) {
305             case INTEGER:
306                 return new IntegerPropertyInputField(value, this, bgColor);
307             case DECIMAL:
308                 return new DecimalPropertyInputField(value, this, bgColor);
309             case DATETIME:
310                 return new DateTimePropertyInputField(value, this, bgColor);
311             case BOOLEAN:
312                 return new BooleanPropertyInputField(value, this, bgColor);
313             default:
314                 return new StringPropertyInputField(value, this, bgColor);
315             }
316         }
317 
318         private void updatePositions() {
319             int n = valueComponents.size();
320             for (int i = 0; i < n; i++) {
321                 MultiValuePropertyInputField comp = (MultiValuePropertyInputField) valueComponents.get(i);
322                 comp.updatePosition(i, i + 1 == n);
323             }
324         }
325 
326         public void addNewValue() {
327             JComponent valueField = new MultiValuePropertyInputField(createInputField(null), this, bgColor);
328             valueComponents.add(valueField);
329             add(valueField, getComponentCount() - 1);
330 
331             updatePositions();
332             setStatus(StatusFlag.Update);
333 
334             revalidate();
335         }
336 
337         public void removeValue(int pos) {
338             remove(valueComponents.remove(pos));
339 
340             updatePositions();
341             setStatus(StatusFlag.Update);
342 
343             revalidate();
344         }
345 
346         public void moveUp(int pos) {
347             JComponent comp = valueComponents.get(pos);
348             Collections.swap(valueComponents, pos, pos - 1);
349 
350             remove(comp);
351             add(comp, pos);
352 
353             updatePositions();
354             setStatus(StatusFlag.Update);
355 
356             revalidate();
357         }
358 
359         public void moveDown(int pos) {
360             JComponent comp = valueComponents.get(pos);
361             Collections.swap(valueComponents, pos, pos + 1);
362 
363             remove(comp);
364             add(comp, pos + 2);
365 
366             updatePositions();
367             setStatus(StatusFlag.Update);
368 
369             revalidate();
370         }
371 
372         public String getId() {
373             return propDef.getId();
374         }
375 
376         public Object getValue() {
377             Object result = null;
378 
379             if (getStatus() == StatusFlag.Update) {
380                 try {
381                     if (propDef.getCardinality() == Cardinality.SINGLE) {
382                         result = ((PropertyValue) valueComponents.get(0)).getPropertyValue();
383                     } else {
384                         List<Object> list = new ArrayList<Object>();
385                         for (JComponent comp : valueComponents) {
386                             list.add(((PropertyValue) comp).getPropertyValue());
387                         }
388                         result = list;
389                     }
390                 } catch (Exception ex) {
391                     ClientHelper.showError(this, ex);
392                 }
393             }
394 
395             return result;
396         }
397 
398         public boolean includeInUpdate() {
399             return getStatus() != StatusFlag.DontChange;
400         }
401 
402         public void setStatus(StatusFlag status) {
403             switch (status) {
404             case Update:
405                 changeBox.setSelectedIndex(1);
406                 break;
407             case Unset:
408                 changeBox.setSelectedIndex(2);
409                 break;
410             default:
411                 changeBox.setSelectedIndex(0);
412             }
413         }
414 
415         public StatusFlag getStatus() {
416             switch (changeBox.getSelectedIndex()) {
417             case 1:
418                 return StatusFlag.Update;
419             case 2:
420                 return StatusFlag.Unset;
421             default:
422                 return StatusFlag.DontChange;
423             }
424         }
425 
426         public Dimension getMaximumSize() {
427             Dimension size = getPreferredSize();
428             size.width = Short.MAX_VALUE;
429             return size;
430         }
431     }
432 
433     /**
434      * Property value interface.
435      */
436     public interface PropertyValue {
437         Object getPropertyValue() throws Exception;
438     }
439 
440     /**
441      * String property.
442      */
443     public static class StringPropertyInputField extends JTextField implements PropertyValue {
444         private static final long serialVersionUID = 1L;
445 
446         public StringPropertyInputField(final Object value, final UpdateStatus status, final Color bgColor) {
447             super(value == null ? "" : value.toString());
448 
449             addKeyListener(new KeyListener() {
450                 @Override
451                 public void keyTyped(KeyEvent e) {
452                 }
453 
454                 @Override
455                 public void keyReleased(KeyEvent e) {
456                     status.setStatus(StatusFlag.Update);
457                 }
458 
459                 @Override
460                 public void keyPressed(KeyEvent e) {
461                 }
462             });
463         }
464 
465         public Object getPropertyValue() {
466             return getText();
467         }
468     }
469 
470     /**
471      * Formatted property.
472      */
473     public static class AbstractFormattedPropertyInputField extends JFormattedTextField implements PropertyValue {
474         private static final long serialVersionUID = 1L;
475 
476         public AbstractFormattedPropertyInputField(final Object value, final Format format, final UpdateStatus status,
477                 final Color bgColor) {
478             super(format);
479             if (value != null) {
480                 setValue(value);
481             }
482 
483             addKeyListener(new KeyListener() {
484                 @Override
485                 public void keyTyped(KeyEvent e) {
486                 }
487 
488                 @Override
489                 public void keyReleased(KeyEvent e) {
490                     status.setStatus(StatusFlag.Update);
491                 }
492 
493                 @Override
494                 public void keyPressed(KeyEvent e) {
495                 }
496             });
497         }
498 
499         public Object getPropertyValue() throws Exception {
500             commitEdit();
501             return getValue();
502         }
503     }
504 
505     /**
506      * Integer property.
507      */
508     public static class IntegerPropertyInputField extends AbstractFormattedPropertyInputField {
509         private static final long serialVersionUID = 1L;
510 
511         public IntegerPropertyInputField(final Object value, final UpdateStatus status, final Color bgColor) {
512             super(value, createFormat(), status, bgColor);
513             setHorizontalAlignment(JTextField.RIGHT);
514         }
515 
516         private static DecimalFormat createFormat() {
517             DecimalFormat result = new DecimalFormat("#,##0");
518             result.setParseBigDecimal(true);
519             result.setParseIntegerOnly(true);
520             return result;
521         }
522 
523         public Object getPropertyValue() throws Exception {
524             return ((BigDecimal) super.getValue()).toBigIntegerExact();
525         }
526     }
527 
528     /**
529      * Decimal property.
530      */
531     public static class DecimalPropertyInputField extends AbstractFormattedPropertyInputField {
532         private static final long serialVersionUID = 1L;
533 
534         public DecimalPropertyInputField(final Object value, final UpdateStatus status, final Color bgColor) {
535             super(value, createFormat(), status, bgColor);
536             setHorizontalAlignment(JTextField.RIGHT);
537         }
538 
539         private static DecimalFormat createFormat() {
540             DecimalFormat result = new DecimalFormat("#,##0.#############################");
541             result.setParseBigDecimal(true);
542             return result;
543         }
544     }
545 
546     /**
547      * Boolean property.
548      */
549     public static class BooleanPropertyInputField extends JComboBox implements PropertyValue {
550         private static final long serialVersionUID = 1L;
551 
552         public BooleanPropertyInputField(final Object value, final UpdateStatus status, final Color bgColor) {
553             super(new Object[] { true, false });
554             setSelectedItem(value == null ? true : value);
555 
556             addActionListener(new ActionListener() {
557                 @Override
558                 public void actionPerformed(ActionEvent e) {
559                     status.setStatus(StatusFlag.Update);
560                 }
561             });
562         }
563 
564         public Object getPropertyValue() {
565             return getSelectedItem();
566         }
567     }
568 
569     /**
570      * DateTime property.
571      */
572     public static class DateTimePropertyInputField extends JPanel implements PropertyValue {
573         private static final long serialVersionUID = 1L;
574 
575         private static final String[] MONTH_STRINGS;
576 
577         static {
578             String[] months = new java.text.DateFormatSymbols().getMonths();
579             int lastIndex = months.length - 1;
580 
581             if (months[lastIndex] == null || months[lastIndex].length() <= 0) {
582                 String[] monthStrings = new String[lastIndex];
583                 System.arraycopy(months, 0, monthStrings, 0, lastIndex);
584                 MONTH_STRINGS = monthStrings;
585             } else {
586                 MONTH_STRINGS = months;
587             }
588         }
589 
590         private final SpinnerNumberModel day;
591         private final SpinnerListModel month;
592         private final SpinnerNumberModel year;
593         private final SpinnerNumberModel hour;
594         private final SpinnerNumberModel min;
595         private final SpinnerNumberModel sec;
596         private final TimeZone timezone;
597 
598         public DateTimePropertyInputField(final Object value, final UpdateStatus status, final Color bgColor) {
599             setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
600             setBackground(bgColor);
601 
602             GregorianCalendar cal = (value == null ? new GregorianCalendar() : (GregorianCalendar) value);
603             timezone = cal.getTimeZone();
604 
605             day = new SpinnerNumberModel(cal.get(Calendar.DATE), 1, 31, 1);
606             addSpinner(new JSpinner(day), status);
607 
608             month = new SpinnerListModel(MONTH_STRINGS);
609             month.setValue(MONTH_STRINGS[cal.get(Calendar.MONTH)]);
610             JSpinner monthSpinner = new JSpinner(month);
611             JComponent editor = monthSpinner.getEditor();
612             if (editor instanceof JSpinner.DefaultEditor) {
613                 JFormattedTextField tf = ((JSpinner.DefaultEditor) editor).getTextField();
614                 tf.setColumns(6);
615                 tf.setHorizontalAlignment(JTextField.RIGHT);
616             }
617             addSpinner(monthSpinner, status);
618 
619             year = new SpinnerNumberModel(cal.get(Calendar.YEAR), 0, 9999, 1);
620             JSpinner yearSpinner = new JSpinner(year);
621             yearSpinner.setEditor(new JSpinner.NumberEditor(yearSpinner, "#"));
622             yearSpinner.getEditor().setBackground(bgColor);
623             addSpinner(yearSpinner, status);
624 
625             add(new JLabel("  "));
626 
627             hour = new SpinnerNumberModel(cal.get(Calendar.HOUR_OF_DAY), 0, 23, 1);
628             JSpinner hourSpinner = new JSpinner(hour);
629             addSpinner(hourSpinner, status);
630 
631             add(new JLabel(":"));
632 
633             min = new SpinnerNumberModel(cal.get(Calendar.MINUTE), 0, 59, 1);
634             JSpinner minSpinner = new JSpinner(min);
635             addSpinner(minSpinner, status);
636 
637             add(new JLabel(":"));
638 
639             sec = new SpinnerNumberModel(cal.get(Calendar.SECOND), 0, 59, 1);
640             JSpinner secSpinner = new JSpinner(sec);
641             addSpinner(secSpinner, status);
642 
643             add(new JLabel(" " + timezone.getDisplayName(true, TimeZone.SHORT)));
644         }
645 
646         private void addSpinner(final JSpinner spinner, final UpdateStatus status) {
647             spinner.addChangeListener(new ChangeListener() {
648                 @Override
649                 public void stateChanged(ChangeEvent e) {
650                     status.setStatus(StatusFlag.Update);
651                 }
652             });
653 
654             add(spinner);
655         }
656 
657         public Object getPropertyValue() {
658             GregorianCalendar result = new GregorianCalendar();
659 
660             result.setTimeZone(timezone);
661 
662             result.set(Calendar.YEAR, year.getNumber().intValue());
663             int mi = 0;
664             String ms = month.getValue().toString();
665             for (int i = 0; i < MONTH_STRINGS.length; i++) {
666                 if (MONTH_STRINGS[i].equals(ms)) {
667                     mi = i;
668                     break;
669                 }
670             }
671             result.set(Calendar.MONTH, mi);
672             result.set(Calendar.DATE, day.getNumber().intValue());
673             result.set(Calendar.HOUR_OF_DAY, hour.getNumber().intValue());
674             result.set(Calendar.MINUTE, min.getNumber().intValue());
675             result.set(Calendar.SECOND, sec.getNumber().intValue());
676 
677             return result;
678         }
679     }
680 
681     /**
682      * Multi value property.
683      */
684     public static class MultiValuePropertyInputField extends JPanel implements PropertyValue {
685         private static final long serialVersionUID = 1L;
686 
687         private static final ImageIcon ICON_UP = ClientHelper.getIcon("up.png");
688         private static final ImageIcon ICON_DOWN = ClientHelper.getIcon("down.png");
689         private static final ImageIcon ICON_REMOVE = ClientHelper.getIcon("remove.png");
690 
691         private final JComponent component;
692         private int position;
693 
694         private JButton upButton;
695         private JButton downButton;
696 
697         public MultiValuePropertyInputField(final JComponent component, final MultivalueManager mutlivalueManager,
698                 final Color bgColor) {
699             super();
700             this.component = component;
701 
702             setLayout(new BorderLayout());
703             setBackground(bgColor);
704 
705             add(component, BorderLayout.CENTER);
706 
707             JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 0, 0));
708             buttonPanel.setBackground(bgColor);
709 
710             upButton = new JButton(ICON_UP);
711             upButton.addActionListener(new ActionListener() {
712                 @Override
713                 public void actionPerformed(ActionEvent e) {
714                     mutlivalueManager.moveUp(MultiValuePropertyInputField.this.position);
715                 }
716             });
717             buttonPanel.add(upButton);
718 
719             downButton = new JButton(ICON_DOWN);
720             downButton.addActionListener(new ActionListener() {
721                 @Override
722                 public void actionPerformed(ActionEvent e) {
723                     mutlivalueManager.moveDown(MultiValuePropertyInputField.this.position);
724                 }
725             });
726             buttonPanel.add(downButton);
727 
728             JButton removeButton = new JButton(ICON_REMOVE);
729             removeButton.addActionListener(new ActionListener() {
730                 @Override
731                 public void actionPerformed(ActionEvent e) {
732                     mutlivalueManager.removeValue(MultiValuePropertyInputField.this.position);
733                 }
734             });
735             buttonPanel.add(removeButton);
736 
737             add(buttonPanel, BorderLayout.LINE_END);
738         }
739 
740         public void updatePosition(int position, boolean isLast) {
741             this.position = position;
742             upButton.setEnabled(position > 0);
743             downButton.setEnabled(!isLast);
744         }
745 
746         public Object getPropertyValue() throws Exception {
747             return ((PropertyValue) component).getPropertyValue();
748         }
749     }
750 }