/* Copyright (c) 2010, Carl Burch. License information is located in the
* com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */
package com.cburch.logisim.circuit;
import java.awt.Graphics;
import java.io.PrintStream;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import com.cburch.logisim.circuit.appear.CircuitAppearance;
import com.cburch.logisim.comp.Component;
import com.cburch.logisim.comp.ComponentDrawContext;
import com.cburch.logisim.comp.ComponentEvent;
import com.cburch.logisim.comp.ComponentFactory;
import com.cburch.logisim.comp.ComponentListener;
import com.cburch.logisim.comp.EndData;
import com.cburch.logisim.data.AttributeSet;
import com.cburch.logisim.data.BitWidth;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.data.Location;
import com.cburch.logisim.std.wiring.Clock;
import com.cburch.logisim.util.CollectionUtil;
import com.cburch.logisim.util.EventSourceWeakSupport;
public class Circuit {
private static final PrintStream DEBUG_STREAM = null;
private class EndChangedTransaction extends CircuitTransaction {
private Component comp;
private Map<Location,EndData> toRemove;
private Map<Location,EndData> toAdd;
EndChangedTransaction(Component comp, Map<Location,EndData> toRemove,
Map<Location,EndData> toAdd) {
this.comp = comp;
this.toRemove = toRemove;
this.toAdd = toAdd;
}
@Override
protected Map<Circuit,Integer> getAccessedCircuits() {
return Collections.singletonMap(Circuit.this, READ_WRITE);
}
@Override
protected void run(CircuitMutator mutator) {
for (Location loc : toRemove.keySet()) {
EndData removed = toRemove.get(loc);
EndData replaced = toAdd.remove(loc);
if (replaced == null) {
wires.remove(comp, removed);
} else if (!replaced.equals(removed)) {
wires.replace(comp, removed, replaced);
}
}
for (EndData end : toAdd.values()) {
wires.add(comp, end);
}
((CircuitMutatorImpl) mutator).markModified(Circuit.this);
}
}
private class MyComponentListener implements ComponentListener {
@Override
public void endChanged(ComponentEvent e) {
locker.checkForWritePermission("ends changed");
Component comp = e.getSource();
HashMap<Location,EndData> toRemove = toMap(e.getOldData());
HashMap<Location,EndData> toAdd = toMap(e.getData());
EndChangedTransaction xn = new EndChangedTransaction(comp, toRemove, toAdd);
locker.execute(xn);
fireEvent(CircuitEvent.ACTION_INVALIDATE, comp);
}
private HashMap<Location,EndData> toMap(Object val) {
HashMap<Location,EndData> map = new HashMap<Location,EndData>();
if (val instanceof List) {
@SuppressWarnings("unchecked")
List<EndData> valList = (List<EndData>) val;
for (EndData end : valList) {
if (end != null) {
map.put(end.getLocation(), end);
}
}
} else if (val instanceof EndData) {
EndData end = (EndData) val;
map.put(end.getLocation(), end);
}
return map;
}
@Override
public void componentInvalidated(ComponentEvent e) {
fireEvent(CircuitEvent.ACTION_INVALIDATE, e.getSource());
}
}
private MyComponentListener myComponentListener = new MyComponentListener();
private CircuitAppearance appearance;
private AttributeSet staticAttrs;
private SubcircuitFactory subcircuitFactory;
private EventSourceWeakSupport<CircuitListener> listeners
= new EventSourceWeakSupport<CircuitListener>();
// doesn't include wires
private HashSet<Component> comps = new HashSet<Component>();
CircuitWires wires = new CircuitWires();
// wires is package-protected for CircuitState and Analyze only.
private ArrayList<Component> clocks = new ArrayList<Component>();
private CircuitLocker locker;
private WeakHashMap<Component, Circuit> circuitsUsingThis;
public Circuit(String name) {
appearance = new CircuitAppearance(this);
staticAttrs = CircuitAttributes.createBaseAttrs(this, name);
subcircuitFactory = new SubcircuitFactory(this);
locker = new CircuitLocker();
circuitsUsingThis = new WeakHashMap<Component, Circuit>();
}
CircuitLocker getLocker() {
return locker;
}
public Collection<Circuit> getCircuitsUsingThis() {
return circuitsUsingThis.values();
}
public void mutatorClear() {
locker.checkForWritePermission("clear");
Set<Component> oldComps = comps;
comps = new HashSet<Component>();
wires = new CircuitWires();
clocks.clear();
for (Component comp : oldComps) {
if (comp.getFactory() instanceof SubcircuitFactory) {
SubcircuitFactory sub = (SubcircuitFactory) comp.getFactory();
sub.getSubcircuit().circuitsUsingThis.remove(comp);
}
}
fireEvent(CircuitEvent.ACTION_CLEAR, oldComps);
}
@Override
public String toString() {
return staticAttrs.getValue(CircuitAttributes.NAME_ATTR);
}
public AttributeSet getStaticAttributes() {
return staticAttrs;
}
//
// Listener methods
//
public void addCircuitListener(CircuitListener what) {
listeners.add(what);
}
public void removeCircuitListener(CircuitListener what) {
listeners.remove(what);
}
void fireEvent(int action, Object data) {
fireEvent(new CircuitEvent(action, this, data));
}
private void fireEvent(CircuitEvent event) {
for (CircuitListener l : listeners) {
l.circuitChanged(event);
}
}
//
// access methods
//
public String getName() {
return staticAttrs.getValue(CircuitAttributes.NAME_ATTR);
}
public CircuitAppearance getAppearance() {
return appearance;
}
public SubcircuitFactory getSubcircuitFactory() {
return subcircuitFactory;
}
public Set<WidthIncompatibilityData> getWidthIncompatibilityData() {
return wires.getWidthIncompatibilityData();
}
public BitWidth getWidth(Location p) {
return wires.getWidth(p);
}
public Location getWidthDeterminant(Location p) {
return wires.getWidthDeterminant(p);
}
public boolean hasConflict(Component comp) {
return wires.points.hasConflict(comp);
}
public Component getExclusive(Location loc) {
return wires.points.getExclusive(loc);
}
private Set<Component> getComponents() {
return CollectionUtil.createUnmodifiableSetUnion(comps, wires.getWires());
}
public boolean contains(Component c) {
return comps.contains(c) || wires.getWires().contains(c);
}
public Set<Wire> getWires() {
return wires.getWires();
}
public Set<Component> getNonWires() {
return comps;
}
public Collection<? extends Component> getComponents(Location loc) {
return wires.points.getComponents(loc);
}
public Collection<? extends Component> getSplitCauses(Location loc) {
return wires.points.getSplitCauses(loc);
}
public Collection<Wire> getWires(Location loc) {
return wires.points.getWires(loc);
}
public Collection<? extends Component> getNonWires(Location loc) {
return wires.points.getNonWires(loc);
}
public boolean isConnected(Location loc, Component ignore) {
for (Component o : wires.points.getComponents(loc)) {
if (o != ignore) {
return true;
}
}
return false;
}
public Set<Location> getSplitLocations() {
return wires.points.getSplitLocations();
}
public Collection<Component> getAllContaining(Location pt) {
HashSet<Component> ret = new HashSet<Component>();
for (Component comp : getComponents()) {
if (comp.contains(pt)) {
ret.add(comp);
}
}
return ret;
}
public Collection<Component> getAllContaining(Location pt, Graphics g) {
HashSet<Component> ret = new HashSet<Component>();
for (Component comp : getComponents()) {
if (comp.contains(pt, g)) {
ret.add(comp);
}
}
return ret;
}
public Collection<Component> getAllWithin(Bounds bds) {
HashSet<Component> ret = new HashSet<Component>();
for (Component comp : getComponents()) {
if (bds.contains(comp.getBounds())) {
ret.add(comp);
}
}
return ret;
}
public Collection<Component> getAllWithin(Bounds bds, Graphics g) {
HashSet<Component> ret = new HashSet<Component>();
for (Component comp : getComponents()) {
if (bds.contains(comp.getBounds(g))) {
ret.add(comp);
}
}
return ret;
}
public WireSet getWireSet(Wire start) {
return wires.getWireSet(start);
}
public Bounds getBounds() {
Bounds wireBounds = wires.getWireBounds();
Iterator<Component> it = comps.iterator();
if (!it.hasNext()) {
return wireBounds;
}
Component first = it.next();
Bounds firstBounds = first.getBounds();
int xMin = firstBounds.getX();
int yMin = firstBounds.getY();
int xMax = xMin + firstBounds.getWidth();
int yMax = yMin + firstBounds.getHeight();
while (it.hasNext()) {
Component c = it.next();
Bounds bds = c.getBounds();
int x0 = bds.getX(); int x1 = x0 + bds.getWidth();
int y0 = bds.getY(); int y1 = y0 + bds.getHeight();
if (x0 < xMin) {
xMin = x0;
}
if (x1 > xMax) {
xMax = x1;
}
if (y0 < yMin) {
yMin = y0;
}
if (y1 > yMax) {
yMax = y1;
}
}
Bounds compBounds = Bounds.create(xMin, yMin, xMax - xMin, yMax - yMin);
if (wireBounds.getWidth() == 0 || wireBounds.getHeight() == 0) {
return compBounds;
} else {
return compBounds.add(wireBounds);
}
}
public Bounds getBounds(Graphics g) {
Bounds ret = wires.getWireBounds();
int xMin = ret.getX();
int yMin = ret.getY();
int xMax = xMin + ret.getWidth();
int yMax = yMin + ret.getHeight();
if (ret == Bounds.EMPTY_BOUNDS) {
xMin = Integer.MAX_VALUE;
yMin = Integer.MAX_VALUE;
xMax = Integer.MIN_VALUE;
yMax = Integer.MIN_VALUE;
}
for (Component c : comps) {
Bounds bds = c.getBounds(g);
if (bds != null && bds != Bounds.EMPTY_BOUNDS) {
int x0 = bds.getX(); int x1 = x0 + bds.getWidth();
int y0 = bds.getY(); int y1 = y0 + bds.getHeight();
if (x0 < xMin) {
xMin = x0;
}
if (x1 > xMax) {
xMax = x1;
}
if (y0 < yMin) {
yMin = y0;
}
if (y1 > yMax) {
yMax = y1;
}
}
}
if (xMin > xMax || yMin > yMax) {
return Bounds.EMPTY_BOUNDS;
}
return Bounds.create(xMin, yMin, xMax - xMin, yMax - yMin);
}
ArrayList<Component> getClocks() {
return clocks;
}
//
// action methods
//
public void setName(String name) {
staticAttrs.setValue(CircuitAttributes.NAME_ATTR, name);
}
private void showDebug(String message, Object parm) {
PrintStream dest = DEBUG_STREAM;
if (dest != null) {
//OK
dest.println("mutatorAdd");
try {
throw new Exception();
} catch (Exception e) {
//OK
e.printStackTrace(dest);
}
}
}
void mutatorAdd(Component c) {
showDebug("mutatorAdd", c);
locker.checkForWritePermission("add");
if (c instanceof Wire) {
Wire w = (Wire) c;
if (w.getEnd0().equals(w.getEnd1())) {
return;
}
boolean added = wires.add(w);
if (!added) {
return;
}
} else {
// add it into the circuit
boolean added = comps.add(c);
if (!added) {
return;
}
wires.add(c);
ComponentFactory factory = c.getFactory();
if (factory instanceof Clock) {
clocks.add(c);
} else if (factory instanceof SubcircuitFactory) {
SubcircuitFactory subcirc = (SubcircuitFactory) factory;
subcirc.getSubcircuit().circuitsUsingThis.put(c, this);
}
c.addComponentListener(myComponentListener);
}
fireEvent(CircuitEvent.ACTION_ADD, c);
}
void mutatorRemove(Component c) {
showDebug("mutatorRemove", c);
locker.checkForWritePermission("remove");
if (c instanceof Wire) {
wires.remove(c);
} else {
wires.remove(c);
comps.remove(c);
ComponentFactory factory = c.getFactory();
if (factory instanceof Clock) {
clocks.remove(c);
} else if (factory instanceof SubcircuitFactory) {
SubcircuitFactory subcirc = (SubcircuitFactory) factory;
subcirc.getSubcircuit().circuitsUsingThis.remove(c);
}
c.removeComponentListener(myComponentListener);
}
fireEvent(CircuitEvent.ACTION_REMOVE, c);
}
//
// Graphics methods
//
public void draw(ComponentDrawContext context, Collection<Component> hidden) {
Graphics g = context.getGraphics();
Graphics g_copy = g.create();
context.setGraphics(g_copy);
wires.draw(context, hidden);
if (hidden == null || hidden.size() == 0) {
for (Component c : comps) {
Graphics g_new = g.create();
context.setGraphics(g_new);
g_copy.dispose();
g_copy = g_new;
c.draw(context);
}
} else {
for (Component c : comps) {
if (!hidden.contains(c)) {
Graphics g_new = g.create();
context.setGraphics(g_new);
g_copy.dispose();
g_copy = g_new;
try {
c.draw(context);
} catch (RuntimeException e) {
// this is a JAR developer error - display it and move on
e.printStackTrace();
}
}
}
}
context.setGraphics(g);
g_copy.dispose();
}
//
// helper methods for other classes in package
//
public static boolean isInput(Component comp) {
return comp.getEnd(0).getType() != EndData.INPUT_ONLY;
}
}