Incrementador de 8 bits

Cada componente incluido en una librería requiere que se definan dos clases: Una clase, implementando la interfaz del componente (de ahora en adelante Component), define el comportamiento de un único componente; la otra, implementando la interfaz de fabricación del componente (de ahora en adelante ComponentFactory), define el comportamiento global del componente y fabrica los componentes. La relación entre los objetos de estas dos clases se asemeja a la relación entre un objeto y su clase en Java.

Implementar directamente todos los métodos en las interfaces Component y ComponentFactory es más bien tedioso y repetitivo. En la práctica es mucho más conveniente extender de las clases ManagedComponent y AbstractComponentFactory.

La mayor parte de las clases relevantes para definir librerías de componentes se encuentran en tres librerías.

com.cburch.logisim.comp
Contiene clases especialmente relacionadas con la definición de componentes incluyendo los tipos que han sido descritos arriba: Component, ComponentFactory, ManagedComponent y AbstractComponentFactory.
com.cburch.logisim.data
Contiene clases relacionadas con elementos de datos asociados con componentes, como la clase Location que representa puntos sobre el lienzo (pizarra de trabajo) o la clase Value que representa los valores que pueden existir en un cable.
com.cburch.logisim.tools
Contiene clases relacionadas con la definición de herramientas y con la especificación de la interacción entre componentes y herramientas. (Esto sólo es necesario para los componentes más especializados)

ByteIncrementer

Este es un ejemplo muy simple que muestra cuales son los componentes esenciales para la definición de un componente. Para el caso se definirá un incrementador que tiene una entrada de 8 bits y que proporciona a la salida 8 bits cuyo valor será el que se encuentre a la entrada más uno.

Este ejemplo por sí mismo no es suficiente para crear un archivo JAR que funcione; también será necesario que proporciones la clase de la Librería, algo que se muestra en el siguiente apartado de esta guía.

package com.cburch.incr;

import com.cburch.logisim.circuit.CircuitState;
import com.cburch.logisim.comp.ComponentDrawContext;
import com.cburch.logisim.comp.ComponentFactory;
import com.cburch.logisim.comp.EndData;
import com.cburch.logisim.comp.ManagedComponent;
import com.cburch.logisim.data.AttributeSet;
import com.cburch.logisim.data.BitWidth;
import com.cburch.logisim.data.Location;
import com.cburch.logisim.data.Value;

/** Implements a bare-bones custom Logisim component, whose single
 * input is 8 bits wide, and whose output value is one more than
 * the input value, also 8 bits wide. */
class ByteIncrementer extends ManagedComponent {
    // The ManagedComponent class conveniently implements just about
    // all of the required methods. All we have to do is to tell it
    // about the different "ends" (i.e., inputs and outputs to the
    // component) in the constructor, and we need to implement the
    // getFactory, propagate, and draw.
    
    /** The width of a byte. */
    private static final BitWidth BIT_WIDTH = BitWidth.create(8);
    
    /** Constructs a component at the given location, with the given
     * attributes. */
    ByteIncrementer(Location loc, AttributeSet attrs) {
        super(loc, attrs, 2);
        // The third parameter (2) to the parent's constructor indicates how
        // many ends the component has. It's not important that this be
        // precisely right: The ManagedComponent uses an ArrayList to manage
        // the ends.
        
        // Now we tell the ManagedComponent superclass about the ends. We
        // assign each end a distinct index, which is used also below in
        // the propagate method.
        setEnd(0, loc.translate(-30, 0), BIT_WIDTH, EndData.INPUT_ONLY);
        setEnd(1, loc,                   BIT_WIDTH, EndData.OUTPUT_ONLY);
    }
    
    /** Returns the class that generated this component. */
    public ComponentFactory getFactory() {
        return ByteIncrementerFactory.instance;
    }

    /** Recomputes the outputs of this component. The circuitState
     * parameter maintains information about the current state of the
     * circuit. */
    public void propagate(CircuitState circuitState) {
        // Retrieve the current value coming into this component.
        Value in = circuitState.getValue(getEndLocation(0));
        
        // Compute the output.
        Value out;
        if(in.isFullyDefined()) {
            // If all input bits are 0 or 1, our input is a valid number, and
            // so can be our output.
            out = Value.createKnown(BIT_WIDTH, in.toIntValue() + 1);
        } else if(in.isErrorValue()) {
            // If any input bits are "errors" (which usually arise from
            // conflicting values on a wire), then we send out all error bits.
            out = Value.createError(BIT_WIDTH);
        } else {
            // Otherwise, some input bits are unspecified. To keep things
            // simple, we'll indicate that all output bits are also unspecified.
            out = Value.createUnknown(BIT_WIDTH);
        }
        
        // Now propagate the output into the circuit state. The parameters
        // here indicate the location affected, the value sent there, the
        // originating component, and the delay. The delay needs to be positive,
        // and it should bear some resemblance to the component's depth, but
        // the exact value isn't too important. The incrementing component
        // would probably be nine levels deep, so I use that value here.
        circuitState.setValue(getEndLocation(1), out, this, 9);
    }

    /** Draws this component using the data contained in the parameter. */
    public void draw(ComponentDrawContext context) {
        // The ComponentDrawContext class contains several convenience
        // methods for common operations. I've kept the drawing simple
        // by just sticking to these operations.
        context.drawRectangle(this, "+1");
        context.drawPins(this);
    }
}

ByteIncrementerFactory

package com.cburch.incr;

import com.cburch.logisim.comp.AbstractComponentFactory;
import com.cburch.logisim.comp.Component;
import com.cburch.logisim.data.AttributeSet;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.data.Location;

/** The object that manufactures ByteIncrementers. */
class ByteIncrementerFactory extends AbstractComponentFactory {
    // The AbstractComponentFactory parent class conveniently implements
    // just about all the methods we need for ComponentFactory. All we really
    // need are the getName, createComponent, and getOffsetBounds methods
    // here.
    
    /** The sole instance of this class. */
    static final ByteIncrementerFactory instance = new ByteIncrementerFactory();
    
    /** Constructs an instance. There is no reason to have multiple instances
     * of this class, so I make the constructor method private to restrict creation
     * to within this class only. */
    private ByteIncrementerFactory() { }
    
    /** Returns the name of this component class, as it is stored in a file. */
    public String getName() {
        return "Byte Incrementer";
    }

    /** Returns the name of this component class as the user should see it. */
    public String getDisplayName() {
        // This may well be different from what is returned by getName.
        // The two most likely reasons for having different strings are
        // that we decide on a more user-friendly name in a future version
        // but we don't want to change the representation within files (for
        // backwards compatibility), or that we want to adapt to the user's
        // chosen language. (Logisim doesn't support internationalization
        // right now, but it is capable of doing so.)
        return "Incrementer (8-Bit)";
    }

    /** Manufactures and returns a component of this component class. */
    public Component createComponent(Location loc, AttributeSet attrs) {
        return new ByteIncrementer(loc, attrs);
    }

    /** Returns a rectangle indicating where the component would appear
     * if it were dropped at the origin. */
    public Bounds getOffsetBounds(AttributeSet attrs) {
        // In this case, the component is a 30x30 rectangle, with the
        // origin on the midpoint of the east side. So the x-coordinate
        // of the top left corner is -30, the y-coordinate is -15, and
        // of course the width and height are both 30.
        return Bounds.create(-30, -15, 30, 30);
    }
    
    // We could customize the icon associated with the tool by overriding
    // the paintIcon method here.

    // We could also override the drawGhost method to customize the appearance
    // of the "ghost" drawn as the user moves the tool across the canvas. By
    // default, the ghost is a rectangle corresponding to getOffsetBounds. A
    // ByteIncrementer is just such a rectangle, so there's no need to override.
}

Siguiente: Clase de la Librería.