/*
* JaamSim Discrete Event Simulation
* Copyright (C) 2004-2013 Ausenco Engineering Canada Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jaamsim.ui;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.ConcurrentModificationException;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import com.jaamsim.Graphics.PolylineInfo;
import com.jaamsim.basicsim.Entity;
/**
* Class to display information about model objects. <br>
* displays the results of getInfo for an object
*/
public class PropertyBox extends FrameBox {
private static PropertyBox myInstance; // only one instance allowed to be open
private Entity currentEntity;
private final JTabbedPane jTabbedFrame = new JTabbedPane();
private static int MAX_ARRAY_ENTRIES_TO_DISPLAY = 10;
public PropertyBox() {
super("Property Viewer");
setDefaultCloseOperation(FrameBox.DISPOSE_ON_CLOSE);
addWindowListener(FrameBox.getCloseListener("ShowPropertyViewer"));
jTabbedFrame.addChangeListener(new TabListener());
getContentPane().add(jTabbedFrame);
setLocation(GUIFrame.COL4_START, GUIFrame.TOP_START);
setSize(GUIFrame.COL4_WIDTH, GUIFrame.HALF_TOP);
}
/**
* Returns the only instance of the property box
*/
public synchronized static PropertyBox getInstance() {
if (myInstance == null)
myInstance = new PropertyBox();
return myInstance;
}
@Override
public void setEntity(Entity entity) {
if (currentEntity == entity)
return;
int prevTab = jTabbedFrame.getSelectedIndex();
jTabbedFrame.removeAll();
currentEntity = entity;
if (currentEntity == null) {
setTitle("Property Viewer");
return;
}
setTitle("Property Viewer - " + currentEntity.getName());
ArrayList<ClassFields> cFields = getFields(entity);
for (int i = 0; i < cFields.size(); i++) {
// The properties in the current page
ClassFields cf = cFields.get(i);
PropertyTableModel mod = new PropertyTableModel(entity, cf.fields);
PropertyTable tab = new PropertyTable(mod);
JScrollPane scroll = new JScrollPane(tab);
jTabbedFrame.addTab(cf.klass.getSimpleName(), scroll);
}
if( prevTab > -1 && jTabbedFrame.getTabCount() > prevTab - 1 ) {
jTabbedFrame.setSelectedIndex(prevTab);
}
}
@Override
public void updateValues(double simTime) {
if(currentEntity == null)
return;
JTable propTable = (JTable)(((JScrollPane)jTabbedFrame.getSelectedComponent()).getViewport().getComponent(0));
((PropertyTableModel)propTable.getModel()).fireTableDataChanged();
}
private synchronized static void killInstance() {
myInstance = null;
}
@Override
public void dispose() {
killInstance();
super.dispose();
}
private static class TabListener implements ChangeListener {
@Override
public void stateChanged(ChangeEvent e) {
GUIFrame.updateUI();
}
}
private static class ClassFields implements Comparator<Field> {
final Class<?> klass;
final ArrayList<Field> fields;
ClassFields(Class<?> aKlass) {
klass = aKlass;
Field[] myFields = klass.getDeclaredFields();
fields = new ArrayList<>(myFields.length);
for (Field each : myFields) {
String name = each.getName();
// Static variables are all capitalized (ignore them)
if (name.toUpperCase().equals(name))
continue;
fields.add(each);
}
Collections.sort(fields, this);
}
@Override
public int compare(Field f1, Field f2) {
return f1.getName().compareToIgnoreCase(f2.getName());
}
}
private static ArrayList<ClassFields> getFields(Entity object) {
if (object == null)
return new ArrayList<>(0);
return getFields(object.getClass());
}
private static ArrayList<ClassFields> getFields(Class<?> klass) {
if (klass == null || klass.getSuperclass() == null)
return new ArrayList<>();
ArrayList<ClassFields> cFields = getFields(klass.getSuperclass());
cFields.add(new ClassFields(klass));
return cFields;
}
static String format(Object value) {
if (value == null)
return "<null>";
if (value instanceof double[])
return Arrays.toString((double[])value);
if (value instanceof int[])
return Arrays.toString((int[])value);
if (value instanceof long[])
return Arrays.toString((long[])value);
if (value instanceof PolylineInfo[]) {
return Arrays.toString((PolylineInfo[])value);
}
try {
// ArrayLists must be converted to a String one element at a time
if (value instanceof ArrayList) {
@SuppressWarnings("unchecked")
ArrayList<Object> array = (ArrayList<Object>) value;
if (array.isEmpty())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append("[");
if (array.get(0) == null) {
sb.append("null");
}
else {
sb.append(array.get(0).toString());
}
int n = Math.min(MAX_ARRAY_ENTRIES_TO_DISPLAY, array.size());
for (int i=1; i<n; i++) {
sb.append(", ");
if (array.get(i) == null) {
sb.append("null");
}
else {
sb.append(array.get(i).toString());
}
}
if (n < array.size())
sb.append(", ... ");
sb.append("]");
return sb.toString();
}
// Every other type of object
return value.toString();
}
catch (ConcurrentModificationException e) {
return "";
}
}
private static class PropertyTable extends JTable {
static int col1Width = 150;
static int col2Width = 100;
static int col3Width = 100;
PropertyTable(TableModel model) {
super(model);
setDefaultRenderer(Object.class, colRenderer);
getColumnModel().getColumn(0).setWidth(col1Width);
getColumnModel().getColumn(1).setWidth(col2Width);
getColumnModel().getColumn(2).setWidth(col3Width);
getTableHeader().setFont(FrameBox.boldFont);
getTableHeader().setReorderingAllowed(false);
}
@Override
public void doLayout() {
col1Width = getColumnModel().getColumn(0).getWidth();
col2Width = getColumnModel().getColumn(1).getWidth();
col3Width = getColumnModel().getColumn(2).getWidth();
FrameBox.fitTableToLastColumn(this);
}
}
private static class PropertyTableModel extends AbstractTableModel {
Entity ent;
ArrayList<Field> fields;
PropertyTableModel(Entity e, ArrayList<Field> cf) {
ent = e;
fields = cf;
}
@Override
public int getColumnCount() {
return 3;
}
@Override
public String getColumnName(int column) {
switch (column) {
case 0: return "Property";
case 1: return "Type";
case 2: return "Value";
}
return "Unknown";
}
@Override
public int getRowCount() {
return fields.size();
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return false;
}
@Override
public Object getValueAt(int row, int col) {
Field field = fields.get(row);
if (col == 0)
return field.getName();
if (col == 1)
return field.getType().getSimpleName();
try {
// save the field's accessibility, reset after reading
boolean accessible = field.isAccessible();
field.setAccessible(true);
Object o = field.get(ent);
field.setAccessible(accessible);
return format(o);
}
catch (SecurityException e) {}
catch (IllegalArgumentException e) {}
catch (IllegalAccessException e) {}
return "";
}
}
}