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

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