SquirrelJME/SquirrelJME

View on GitHub
tools/squirreljme-debugger/src/main/java/cc/squirreljme/debugger/InspectKnownValue.java

Summary

Maintainability
A
0 mins
Test Coverage
// -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
// ---------------------------------------------------------------------------
// Multi-Phasic Applications: SquirrelJME
//     Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
// ---------------------------------------------------------------------------
// SquirrelJME is under the Mozilla Public License Version 2.0.
// See license.mkd for licensing and copyright information.
// ---------------------------------------------------------------------------

package cc.squirreljme.debugger;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Window;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.Objects;
import java.util.function.BiFunction;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;

/**
 * Component for inspecting a known value.
 *
 * @since 2024/01/20
 */
public class InspectKnownValue
    extends JPanel
{
    /** The updater for the value. */
    protected final BiFunction<JComponent, KnownValue<?>, JComponent> updater;
    
    /** The base root component. */
    protected final JComponent base;
    
    /** The owning window. */
    protected final Window owner;
    
    /** The state of the debugger. */
    protected final DebuggerState state;
    
    /** The value being inspected. */
    private final Reference<KnownValue<?>> _value;
    
    /**
     * Initializes the inspector.
     *
     * @param __owner The owning window, is optional.
     * @param __state The debugger state.
     * @param __value The value to inspect.
     * @throws NullPointerException On null arguments.
     * @since 2024/01/20
     */
    public InspectKnownValue(Window __owner, DebuggerState __state,
        KnownValue<?> __value)
        throws NullPointerException
    {
        if (__state == null || __value == null)
            throw new NullPointerException("NARG");
        
        // Store owner and state for later usage
        this.owner = __owner;
        this.state = __state;
        
        // Set value
        this._value = new WeakReference<>(__value);
        
        // Determine how this updates...
        BiFunction<JComponent, KnownValue<?>, JComponent> updater;
        if (__value.type == Boolean.class)
            updater = this::__updateBoolean;
        else if (__value.type == InfoByteCode.class)
            updater = this::__updateByteCode;
        else if (__value.type == InfoMethod[].class)
            updater = this::__updateMethods;
        else if (__value.type == String.class)
            updater = this::__updateString;
        else if (Number.class.isAssignableFrom(__value.type))
            updater = this::__updateNumber;
        else
            updater = this::__updateUnknown;
        
        // Setup initial item and updater
        JComponent base = updater.apply(null, __value);
        
        // Add item to the center of this panel
        FlowLayout layout = new FlowLayout(FlowLayout.LEADING);
        layout.setVgap(0);
        layout.setHgap(0);
        layout.setAlignment(FlowLayout.LEADING);
        this.setLayout(layout);
        this.add(base);
        
        // Store for later usage and updating
        this.base = base;
        this.updater = updater;
    }
    
    /**
     * Updates the given value.
     *
     * @since 2024/01/20
     */
    public void update()
    {
        KnownValue<?> value = this.__value();
        if (value != null)
            this.updater.apply(this.base, value);
    }
    
    /**
     * Updates the boolean value.
     *
     * @param __base The base component.
     * @param __value The value to show.
     * @return The component used.
     * @since 2024/01/20
     */
    private JComponent __updateBoolean(JComponent __base,
        KnownValue<?> __value)
    {
        // Need to initialize?
        JCheckBox check;
        if (__base != null)
            check = (JCheckBox)__base;
        else
        {
            check = new JCheckBox();
            check.setEnabled(false);
        }
        
        // Set label value
        if (!__value.isKnown())
            check.setText("Unknown?");
        else
        {
            check.setSelected((Boolean)__value.get());
            check.setText("");
        }
        
        return check;
    }
    
    /**
     * Initializes a byte code viewer.
     *
     * @param __base The base component.
     * @param __value The byte code value.
     * @return The component used to show the byte code.
     * @since 2024/01/24
     */
    private JComponent __updateByteCode(JComponent __base,
        KnownValue<?> __value)
    {
        // Need to initialize?
        JButton button;
        if (__base != null)
            button = (JButton)__base;
        else
        {
            button = new JButton();
            
            // Add handler for showing the byte code
            button.addActionListener((__event) -> {
                InfoByteCode byteCode = __value.get(InfoByteCode.class);
                if (byteCode == null)
                    return;
                
                // Was this garbage collected?
                InfoMethod infoMethod = byteCode.method.get();
                if (infoMethod == null)
                    return;
                
                // Show dialog for the byte code
                ShownMethodDialog dialog = new ShownMethodDialog(
                    this.owner, this.state, new RemoteMethodViewer(this.state,
                        infoMethod));
                dialog.setLocationRelativeTo(null);
                dialog.setVisible(true);
            });
        }
        
        // Set label value
        if (!__value.isKnown())
            button.setText("Unknown?");
        else
            button.setText("Show Bytecode");
        
        // Return self
        return button;
    }
    
    /**
     * Updates the method value.
     *
     * @param __base The base component.
     * @param __value The value used.
     * @return The resultant component.
     * @since 2024/01/24
     */
    private JComponent __updateMethods(JComponent __base,
        KnownValue<?> __value)
    {
        // Need to initialize?
        JButton button;
        if (__base != null)
            button = (JButton)__base;
        else
        {
            button = new JButton();
            
            // Show list of methods when selecting the button, initialize this
            // here because otherwise there would be multiple actions ran
            // every time there was an update
            button.addActionListener((__event) -> {
                InfoMethod[] methods = __value.get(InfoMethod[].class);
                if (methods == null)
                    return;
                
                // Update all methods
                DebuggerState state = this.state;
                for (InfoMethod method : methods)
                    method.update(state, null);
                
                // Show inspector on it
                Utils.inspect(this.owner, state,
                    InfoKind.METHOD, methods);
            });
        }
        
        // Set label value
        if (!__value.isKnown())
            button.setText("Unknown?");
        else
        {
            // Get all the methods
            InfoMethod[] methods = __value.get(InfoMethod[].class);
            
            // The button text is just the number of methods
            button.setText(String.format("%d Methods", methods.length));
        }
        
        // Return self
        return button;
    }
    
    /**
     * Updates the number value.
     *
     * @param __base The base component.
     * @param __value The value of the number.
     * @return The resultant component.
     * @since 2024/01/25
     */
    private JComponent __updateNumber(JComponent __base, KnownValue<?> __value)
    {
        // Need to initialize?
        JTextField text;
        if (__base != null)
            text = (JTextField)__base;
        else
        {
            text = new JTextField();
            text.setEditable(false);
            text.setBorder(new EmptyBorder(0, 0, 0, 0));
        }
        
        // Set label value
        if (!__value.isKnown())
            text.setText("Unknown?");
        else
            text.setText(Long.toString(
                ((Number)__value.get()).longValue(), 10));
        
        return text;
    }
    
    /**
     * Updates the string value.
     *
     * @param __base The base component.
     * @param __value The value to show.
     * @return The component used.
     * @since 2024/01/20
     */
    private JComponent __updateString(JComponent __base,
        KnownValue<?> __value)
    {
        // Need to initialize?
        JTextField text;
        if (__base != null)
            text = (JTextField)__base;
        else
        {
            text = new JTextField();
            text.setEditable(false);
            text.setBorder(new EmptyBorder(0, 0, 0, 0));
        }
        
        // Set label value
        if (!__value.isKnown())
            text.setText("Unknown?");
        else
            text.setText((String)__value.get());
        
        return text;
    }
    
    /**
     * Updates the boolean value.
     *
     * @param __base The base component.
     * @param __value The value to show.
     * @return The component used.
     * @since 2024/01/20
     */
    private JComponent __updateUnknown(JComponent __base,
        KnownValue<?> __value)
    {
        // Need to initialize?
        JLabel label;
        if (__base != null)
            label = (JLabel)__base;
        else
            label = new JLabel();
        
        // Set label value
        if (!__value.isKnown())
            label.setText("Unknown?");
        else
            label.setText(Objects.toString(__value.get()));
        
        return label;
    }
    
    /**
     * Returns the stored known value.
     *
     * @return The resultant value.
     * @since 2024/01/21
     */
    private KnownValue<?> __value()
    {
        return this._value.get();
    }
}