/******************************************************************************* * This file is part of logisim-evolution. * * logisim-evolution is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * logisim-evolution is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with logisim-evolution. If not, see <http://www.gnu.org/licenses/>. * * Original code by Carl Burch (http://www.cburch.com), 2011. * Subsequent modifications by : * + Haute École Spécialisée Bernoise * http://www.bfh.ch * + Haute École du paysage, d'ingénierie et d'architecture de Genève * http://hepia.hesge.ch/ * + Haute École d'Ingénierie et de Gestion du Canton de Vaud * http://www.heig-vd.ch/ * The project is currently maintained by : * + REDS Institute - HEIG-VD * Yverdon-les-Bains, Switzerland * http://reds.heig-vd.ch *******************************************************************************/ package com.cburch.logisim.circuit; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import com.cburch.logisim.data.AbstractAttributeSet; import com.cburch.logisim.data.Attribute; import com.cburch.logisim.data.AttributeOption; import com.cburch.logisim.data.Attributes; import com.cburch.logisim.data.BitWidth; import com.cburch.logisim.data.Direction; import com.cburch.logisim.instance.StdAttr; class SplitterAttributes extends AbstractAttributeSet { static class BitOutAttribute extends Attribute<Integer> { int which; BitOutOption[] options; private BitOutAttribute(int which, BitOutOption[] options) { super("bit" + which, Strings.getter("splitterBitAttr", "" + which)); this.which = which; this.options = options; } private BitOutAttribute createCopy() { return new BitOutAttribute(which, options); } @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public java.awt.Component getCellEditor(Integer value) { int index = value.intValue(); javax.swing.JComboBox combo = new javax.swing.JComboBox(options); combo.setSelectedIndex(index); return combo; } public Object getDefault() { return Integer.valueOf(which + 1); } @Override public Integer parse(String value) { if (value.equals(unchosen_val)) { return Integer.valueOf(0); } else { return Integer.valueOf(1 + Integer.parseInt(value)); } } @Override public String toDisplayString(Integer value) { int index = value.intValue(); return options[index].toString(); } @Override public String toStandardString(Integer value) { int index = value.intValue(); if (index == 0) { return unchosen_val; } else { return "" + (index - 1); } } } private static class BitOutOption { int value; boolean isVertical; boolean isLast; BitOutOption(int value, boolean isVertical, boolean isLast) { this.value = value; this.isVertical = isVertical; this.isLast = isLast; } @Override public String toString() { if (value < 0) { return Strings.get("splitterBitNone"); } else { String ret = "" + value; Direction noteDir; if (value == 0) { noteDir = isVertical ? Direction.NORTH : Direction.EAST; } else if (isLast) { noteDir = isVertical ? Direction.SOUTH : Direction.WEST; } else { noteDir = null; } if (noteDir != null) { ret += " (" + noteDir.toVerticalDisplayString() + ")"; } return ret; } } } static byte[] computeDistribution(int fanout, int bits, int order) { byte[] ret = new byte[bits]; if (order >= 0) { if (fanout >= bits) { for (int i = 0; i < bits; i++) ret[i] = (byte) (i + 1); } else { int threads_per_end = bits / fanout; int ends_with_extra = bits % fanout; int cur_end = -1; // immediately increments int left_in_end = 0; for (int i = 0; i < bits; i++) { if (left_in_end == 0) { ++cur_end; left_in_end = threads_per_end; if (ends_with_extra > 0) { ++left_in_end; --ends_with_extra; } } ret[i] = (byte) (1 + cur_end); --left_in_end; } } } else { if (fanout >= bits) { for (int i = 0; i < bits; i++) ret[i] = (byte) (fanout - i); } else { int threads_per_end = bits / fanout; int ends_with_extra = bits % fanout; int cur_end = -1; int left_in_end = 0; for (int i = bits - 1; i >= 0; i--) { if (left_in_end == 0) { ++cur_end; left_in_end = threads_per_end; if (ends_with_extra > 0) { ++left_in_end; --ends_with_extra; } } ret[i] = (byte) (1 + cur_end); --left_in_end; } } } return ret; } public static final AttributeOption APPEAR_LEGACY = new AttributeOption( "legacy", Strings.getter("splitterAppearanceLegacy")); public static final AttributeOption APPEAR_LEFT = new AttributeOption( "left", Strings.getter("splitterAppearanceLeft")); public static final AttributeOption APPEAR_RIGHT = new AttributeOption( "right", Strings.getter("splitterAppearanceRight")); public static final AttributeOption APPEAR_CENTER = new AttributeOption( "center", Strings.getter("splitterAppearanceCenter")); public static final Attribute<AttributeOption> ATTR_APPEARANCE = Attributes .forOption("appear", Strings.getter("splitterAppearanceAttr"), new AttributeOption[] { APPEAR_LEFT, APPEAR_RIGHT, APPEAR_CENTER, APPEAR_LEGACY }); public static final Attribute<BitWidth> ATTR_WIDTH = Attributes .forBitWidth("incoming", Strings.getter("splitterBitWidthAttr")); public static final Attribute<Integer> ATTR_FANOUT = Attributes .forIntegerRange("fanout", Strings.getter("splitterFanOutAttr"), 1, 32); private static final List<Attribute<?>> INIT_ATTRIBUTES = Arrays .asList(new Attribute<?>[] { StdAttr.FACING, ATTR_FANOUT, ATTR_WIDTH, ATTR_APPEARANCE, }); private static final String unchosen_val = "none"; private ArrayList<Attribute<?>> attrs = new ArrayList<Attribute<?>>( INIT_ATTRIBUTES); private SplitterParameters parameters; AttributeOption appear = APPEAR_LEFT; Direction facing = Direction.EAST; byte fanout = 2; // number of ends this splits into byte[] bit_end = new byte[2]; // how each bit maps to an end (0 if nowhere); // other values will be between 1 and fanout BitOutOption[] options = null; SplitterAttributes() { configureOptions(); configureDefaults(); parameters = new SplitterParameters(this); } private void configureDefaults() { int offs = INIT_ATTRIBUTES.size(); int curNum = attrs.size() - offs; // compute default values byte[] dflt = computeDistribution(fanout, bit_end.length, 1); boolean changed = curNum != bit_end.length; // remove excess attributes while (curNum > bit_end.length) { curNum--; attrs.remove(offs + curNum); } // set existing attributes for (int i = 0; i < curNum; i++) { if (bit_end[i] != dflt[i]) { BitOutAttribute attr = (BitOutAttribute) attrs.get(offs + i); bit_end[i] = dflt[i]; fireAttributeValueChanged(attr, Integer.valueOf(bit_end[i]),null); } } // add new attributes for (int i = curNum; i < bit_end.length; i++) { BitOutAttribute attr = new BitOutAttribute(i, options); bit_end[i] = dflt[i]; attrs.add(attr); } if (changed) fireAttributeListChanged(); } private void configureOptions() { // compute the set of options for BitOutAttributes options = new BitOutOption[fanout + 1]; boolean isVertical = facing == Direction.EAST || facing == Direction.WEST; for (int i = -1; i < fanout; i++) { options[i + 1] = new BitOutOption(i, isVertical, i == fanout - 1); } // go ahead and set the options for the existing attributes int offs = INIT_ATTRIBUTES.size(); int curNum = attrs.size() - offs; for (int i = 0; i < curNum; i++) { BitOutAttribute attr = (BitOutAttribute) attrs.get(offs + i); attr.options = options; } } @Override protected void copyInto(AbstractAttributeSet destObj) { SplitterAttributes dest = (SplitterAttributes) destObj; dest.parameters = this.parameters; dest.attrs = new ArrayList<Attribute<?>>(this.attrs.size()); dest.attrs.addAll(INIT_ATTRIBUTES); for (int i = INIT_ATTRIBUTES.size(), n = this.attrs.size(); i < n; i++) { BitOutAttribute attr = (BitOutAttribute) this.attrs.get(i); dest.attrs.add(attr.createCopy()); } dest.facing = this.facing; dest.fanout = this.fanout; dest.appear = this.appear; dest.bit_end = this.bit_end.clone(); dest.options = this.options; } @Override public List<Attribute<?>> getAttributes() { return attrs; } Attribute<?> getBitOutAttribute(int index) { return attrs.get(INIT_ATTRIBUTES.size() + index); } public SplitterParameters getParameters() { SplitterParameters ret = parameters; if (ret == null) { ret = new SplitterParameters(this); parameters = ret; } return ret; } @Override @SuppressWarnings("unchecked") public <V> V getValue(Attribute<V> attr) { if (attr == StdAttr.FACING) { return (V) facing; } else if (attr == ATTR_FANOUT) { return (V) Integer.valueOf(fanout); } else if (attr == ATTR_WIDTH) { return (V) BitWidth.create(bit_end.length); } else if (attr == ATTR_APPEARANCE) { return (V) appear; } else if (attr instanceof BitOutAttribute) { BitOutAttribute bitOut = (BitOutAttribute) attr; return (V) Integer.valueOf(bit_end[bitOut.which]); } else { return null; } } @Override public <V> void setValue(Attribute<V> attr, V value) { if (attr == StdAttr.FACING) { Direction NewFacing = (Direction) value; if (facing.equals(NewFacing)) return; facing = (Direction) value; configureOptions(); parameters = null; } else if (attr == ATTR_FANOUT) { int newValue = ((Integer) value).intValue(); byte[] bits = bit_end; for (int i = 0; i < bits.length; i++) { if (bits[i] >= newValue) bits[i] = (byte) (newValue - 1); } if (fanout == (byte) newValue) return; fanout = (byte) newValue; configureOptions(); configureDefaults(); parameters = null; } else if (attr == ATTR_WIDTH) { BitWidth width = (BitWidth) value; if (bit_end.length == width.getWidth()) return; bit_end = new byte[width.getWidth()]; configureOptions(); configureDefaults(); } else if (attr == ATTR_APPEARANCE) { AttributeOption appearance = (AttributeOption) value; if (appear.equals(appearance)) return; appear = appearance; parameters = null; } else if (attr instanceof BitOutAttribute) { BitOutAttribute bitOutAttr = (BitOutAttribute) attr; int val; if (value instanceof Integer) { val = ((Integer) value).intValue(); } else { val = ((BitOutOption) value).value + 1; } if (val >= 0 && val <= fanout) { if (bit_end[bitOutAttr.which] == (byte) val) return; bit_end[bitOutAttr.which] = (byte) val; } else return; } else { throw new IllegalArgumentException("unknown attribute " + attr); } fireAttributeValueChanged(attr, value,null); } }