View Javadoc
1   package org.opentrafficsim.editor;
2   
3   import java.util.Arrays;
4   import java.util.Optional;
5   
6   import javax.swing.JTable;
7   import javax.swing.table.AbstractTableModel;
8   
9   import org.djutils.exceptions.Throw;
10  import org.opentrafficsim.editor.DocumentReader.NodeAnnotation;
11  import org.opentrafficsim.swing.gui.OtsAnimationPanel;
12  import org.w3c.dom.Node;
13  
14  import de.javagl.treetable.JTreeTable;
15  
16  /**
17   * Model for a {@code JTable} to display the attributes of a {@code XsdTreeNode}.
18   * <p>
19   * Copyright (c) 2023-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
20   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
21   * </p>
22   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
23   */
24  public class AttributesTableModel extends AbstractTableModel
25  {
26  
27      /** */
28      private static final long serialVersionUID = 20230217L;
29  
30      /** Column names. */
31      private static final String[] COLUMN_NAMES = new String[] {"Property", "Value", "Use", ""};
32  
33      /** Index of the property column. */
34      public static final int PROPERTY_COLUMN = Arrays.asList(COLUMN_NAMES).indexOf("Property");
35  
36      /** Index of the value column. */
37      public static final int VALUE_COLUMN = Arrays.asList(COLUMN_NAMES).indexOf("Value");
38  
39      /** Index of the use column. */
40      public static final int USE_COLUMN = Arrays.asList(COLUMN_NAMES).indexOf("Use");
41  
42      /** Index of the description column. */
43      public static final int DESCRIPTION_COLUMN = Arrays.asList(COLUMN_NAMES).indexOf("");
44  
45      /** Minimum column widths. */
46      private static final int[] MIN_COLUMN_WIDTHS = new int[] {50, 50, 30, 20};
47  
48      /** Preferred column widths. */
49      private static final int[] PREFERRED_COLUMN_WIDTHS = new int[] {400, 400, 30, 20};
50  
51      /** The node of which the attributes are displayed. */
52      private final XsdTreeNode node;
53  
54      /** Tree table, so it can be updated visually when a value has changed. */
55      private final JTreeTable treeTable;
56  
57      /**
58       * Constructor.
59       * @param node node of which the attributes are displayed.
60       * @param treeTable tree table.
61       */
62      public AttributesTableModel(final XsdTreeNode node, final JTreeTable treeTable)
63      {
64          this.node = node;
65          this.treeTable = treeTable;
66      }
67  
68      @Override
69      public int getRowCount()
70      {
71          return this.node == null ? 0 : this.node.attributeCount();
72      }
73  
74      @Override
75      public int getColumnCount()
76      {
77          return COLUMN_NAMES.length;
78      }
79  
80      @Override
81      public String getColumnName(final int columnIndex)
82      {
83          return COLUMN_NAMES[columnIndex];
84      }
85  
86      @Override
87      public Class<?> getColumnClass(final int columnIndex)
88      {
89          return String.class;
90      }
91  
92      @Override
93      public boolean isCellEditable(final int rowIndex, final int columnIndex)
94      {
95          if (this.node.getPathString().startsWith(XsdPaths.DEFINITIONS)
96                  && "Default".equals(this.node.getAttributeNameByIndex(rowIndex))
97                  && "xsd:boolean".equals(this.node.getAttributeBaseType(rowIndex)))
98          {
99              // disable check boxes regarding the 'Default' status of definitions as definitions edited in the editor never are
100             return false;
101         }
102         return columnIndex == VALUE_COLUMN && !this.node.isIncluded();
103     }
104 
105     @Override
106     public Object getValueAt(final int rowIndex, final int columnIndex)
107     {
108         Node attribute = this.node.getAttributeNode(rowIndex);
109         if (columnIndex == PROPERTY_COLUMN)
110         {
111             return OtsAnimationPanel.separatedName(DocumentReader.getAttribute(attribute, "name").orElse(null));
112         }
113         else if (columnIndex == VALUE_COLUMN)
114         {
115             Object value = this.node.getAttributeValue(rowIndex);
116             return value;
117         }
118         else if (columnIndex == USE_COLUMN)
119         {
120             Optional<String> use = DocumentReader.getAttribute(attribute, "use");
121             return use.isPresent() && use.get().equals("required") ? "*" : "";
122         }
123         else if (columnIndex == DESCRIPTION_COLUMN)
124         {
125             return NodeAnnotation.DESCRIPTION.get(attribute).isPresent() ? "i" : null;
126         }
127         throw new IndexOutOfBoundsException();
128     }
129 
130     @Override
131     public void setValueAt(final Object aValue, final int rowIndex, final int columnIndex)
132     {
133         Throw.when(columnIndex != VALUE_COLUMN, IllegalArgumentException.class,
134                 "Attribute table model requested to set a value from a column that does not represent the attribute value.");
135         if (aValue == null)
136         {
137             return;
138         }
139         if (this.node == null)
140         {
141             // node was deleted
142             return;
143         }
144         this.node.setAttributeValue(rowIndex, aValue.toString());
145         this.treeTable.updateUI();
146         fireTableCellUpdated(rowIndex, columnIndex);
147     }
148 
149     /**
150      * Returns the underlying node for which attributes are shown.
151      * @return underlying node for which attributes are shown.
152      */
153     public XsdTreeNode getNode()
154     {
155         return this.node;
156     }
157 
158     /**
159      * Apply the column widths to a newly created table.
160      * @param attributeTable table.
161      */
162     public static void applyColumnWidth(final JTable attributeTable)
163     {
164         Throw.when(attributeTable.getColumnCount() != COLUMN_NAMES.length, IllegalArgumentException.class,
165                 "The number of columns in the table to show node attributes is not equal to the number of defined columns.");
166         for (int i = 0; i < attributeTable.getColumnCount(); i++)
167         {
168             attributeTable.getColumn(COLUMN_NAMES[i]).setMinWidth(MIN_COLUMN_WIDTHS[i]);
169             attributeTable.getColumn(COLUMN_NAMES[i]).setPreferredWidth(PREFERRED_COLUMN_WIDTHS[i]);
170         }
171     }
172 
173 }