View Javadoc
1   package org.opentrafficsim.editor;
2   
3   import java.rmi.RemoteException;
4   import java.util.Arrays;
5   import java.util.function.Supplier;
6   
7   import javax.swing.SwingUtilities;
8   
9   import org.djutils.eval.Eval;
10  import org.w3c.dom.Document;
11  
12  import de.javagl.treetable.AbstractTreeTableModel;
13  import de.javagl.treetable.JTreeTable;
14  import de.javagl.treetable.TreeTableModel;
15  
16  /**
17   * Defines the columns in the {@code JTreeTable}. Most functionality is forwarded to the tree with {@code XsdTreeNode}'s.
18   * <p>
19   * Copyright (c) 2023-2026 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 Wouter Schakel
23   */
24  public class XsdTreeTableModel extends AbstractTreeTableModel
25  {
26  
27      /** Column names. */
28      private static final String[] COLUMN_NAMES = new String[] {"Item", "Id", "Value", "#"};
29  
30      /** Index of the tree column. */
31      public static final int TREE_COLUMN = Arrays.asList(COLUMN_NAMES).indexOf("Item");
32  
33      /** Index of the Id column. */
34      public static final int ID_COLUMN = Arrays.asList(COLUMN_NAMES).indexOf("Id");
35  
36      /** Index of the Value column. */
37      public static final int VALUE_COLUMN = Arrays.asList(COLUMN_NAMES).indexOf("Value");
38  
39      /** Minimum column widths. */
40      private static final int[] MIN_COLUMN_WIDTHS = new int[] {100, 50, 50, 30};
41  
42      /** Preferred column widths. */
43      private static final int[] PREFERRED_COLUMN_WIDTHS = new int[] {600, 200, 200, 30};
44  
45      /** Column classes. */
46      private static final Class<?>[] COLUMN_CLASSES =
47              new Class<?>[] {TreeTableModel.class, String.class, String.class, String.class};
48  
49      /** Tree table, so it can be updated visually when a value has changed. */
50      private JTreeTable treeTable;
51  
52      /**
53       * Constructor.
54       * @param document XSD document.
55       * @param ignoreChanges whether to ignore changes
56       * @param evalSupplier eval supplier
57       * @throws RemoteException when unable to listen for created nodes.
58       */
59      protected XsdTreeTableModel(final Document document, final Supplier<Boolean> ignoreChanges,
60              final Supplier<Eval> evalSupplier) throws RemoteException
61      {
62          super(document == null ? null : new XsdTreeNodeRoot(new Schema(document), ignoreChanges, evalSupplier));
63      }
64  
65      /**
66       * Sets the tree table.
67       * @param treeTable tree table.
68       */
69      public void setTreeTable(final JTreeTable treeTable)
70      {
71          this.treeTable = treeTable;
72      }
73  
74      @Override
75      public int getColumnCount()
76      {
77          return COLUMN_CLASSES.length;
78      }
79  
80      @Override
81      public String getColumnName(final int column)
82      {
83          return COLUMN_NAMES[column];
84      }
85  
86      @Override
87      public Class<?> getColumnClass(final int column)
88      {
89          return COLUMN_CLASSES[column];
90      }
91  
92      @Override
93      public Object getValueAt(final Object node, final int column)
94      {
95          if (column == TREE_COLUMN)
96          {
97              return node; // required for tree view of column 0
98          }
99          if (column == ID_COLUMN)
100         {
101             if (((XsdTreeNode) node).isIdentifiable())
102             {
103                 return ((XsdTreeNode) node).getId();
104             }
105             return "";
106         }
107         else if (column == VALUE_COLUMN)
108         {
109             if (((XsdTreeNode) node).isEditable())
110             {
111                 return ((XsdTreeNode) node).getValue();
112             }
113             return "";
114         }
115         return occurs(((XsdTreeNode) node).minOccurs(), ((XsdTreeNode) node).maxOccurs());
116     }
117 
118     /**
119      * Creates a string to display minOccurs and maxOccurs.
120      * @param minOccurs minOccurs.
121      * @param maxOccurs maxOccurs.
122      * @return string to display minOccurs and maxOccurs.
123      */
124     public String occurs(final int minOccurs, final int maxOccurs)
125     {
126         if (minOccurs == maxOccurs)
127         {
128             return Integer.toString(minOccurs);
129         }
130         return Integer.valueOf(minOccurs) + ".." + (maxOccurs == -1 ? "∞" : Integer.valueOf(maxOccurs));
131     }
132 
133     @Override
134     public Object getChild(final Object parent, final int index)
135     {
136         return ((XsdTreeNode) parent).getChild(index);
137     }
138 
139     @Override
140     public int getChildCount(final Object parent)
141     {
142         return ((XsdTreeNode) parent).getChildCount();
143     }
144 
145     @Override
146     public boolean isCellEditable(final Object node, final int column)
147     {
148         if (column == TREE_COLUMN)
149         {
150             return true; // required for tree in column 0
151         }
152         XsdTreeNode treeNode = (XsdTreeNode) node;
153         if (column == ID_COLUMN)
154         {
155             return treeNode.isIdentifiable() && !treeNode.isIncluded();
156         }
157         return treeNode.isEditable() && column == VALUE_COLUMN && !treeNode.isIncluded();
158     }
159 
160     @Override
161     public void setValueAt(final Object aValue, final Object node, final int column)
162     {
163         if (column == ID_COLUMN)
164         {
165             ((XsdTreeNode) node).setId(aValue.toString());
166         }
167         else if (column == VALUE_COLUMN)
168         {
169             ((XsdTreeNode) node).setValue(aValue.toString());
170         }
171         // invoking directly results in a NullPointerException when clicking on the table during editing of a value in the table
172         SwingUtilities.invokeLater(() -> this.treeTable.updateUI());
173     }
174 
175     /**
176      * Apply the column widths to a newly created tree table.
177      * @param treeTable tree table.
178      */
179     public static void applyColumnWidth(final JTreeTable treeTable)
180     {
181         for (int i = 0; i < treeTable.getColumnCount(); i++)
182         {
183             treeTable.getColumn(COLUMN_NAMES[i]).setMinWidth(MIN_COLUMN_WIDTHS[i]);
184             treeTable.getColumn(COLUMN_NAMES[i]).setPreferredWidth(PREFERRED_COLUMN_WIDTHS[i]);
185         }
186     }
187 
188 }