/* 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.instance;
import java.awt.Color;
import java.awt.Graphics;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.swing.Icon;
import org.apache.commons.collections15.list.UnmodifiableList;
import com.cburch.logisim.LogisimVersion;
import com.cburch.logisim.circuit.CircuitState;
import com.cburch.logisim.comp.AbstractComponentFactory;
import com.cburch.logisim.comp.Component;
import com.cburch.logisim.comp.ComponentDrawContext;
import com.cburch.logisim.data.Attribute;
import com.cburch.logisim.data.AttributeSet;
import com.cburch.logisim.data.AttributeSets;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.data.Direction;
import com.cburch.logisim.data.Location;
import com.cburch.logisim.gui.log.Loggable;
import com.cburch.logisim.tools.Pokable;
import com.cburch.logisim.tools.key.KeyConfigurator;
import com.cburch.logisim.util.Icons;
import com.cburch.logisim.util.StringUtil;
/**
* Represents a category of components that appear in a circuit. This class
* and <code>Component</code> share the same sort of relationship as the
* relation between <em>classes</em> and <em>instances</em> in Java. Normally,
* there is only one ComponentFactory created for any particular category.
*/
@SuppressWarnings("deprecation")
public abstract class InstanceFactory extends AbstractComponentFactory {
private String name;
private String displayName;
private String defaultToolTip;
private String iconName;
private Icon icon;
private Attribute<?>[] attrs;
private Object[] defaults;
private AttributeSet defaultSet;
private Bounds bounds;
private List<Port> portList;
private Attribute<Direction> facingAttribute;
private Boolean shouldSnap;
private KeyConfigurator keyConfigurator;
private Class<? extends InstancePoker> pokerClass;
private Class<? extends InstanceLogger> loggerClass;
public InstanceFactory(String name) {
this(name, StringUtil.constantGetter(name));
}
public InstanceFactory(String name, String displayName) {
this.name = name;
this.displayName = displayName;
this.iconName = null;
this.icon = null;
this.attrs = null;
this.defaults = null;
this.bounds = Bounds.EMPTY_BOUNDS;
this.portList = Collections.emptyList();
this.keyConfigurator = null;
this.facingAttribute = null;
this.shouldSnap = Boolean.TRUE;
}
@Override
public String getName() {
return name;
}
@Override
public String getDisplayName() {
return getDisplayGetter().toString();
}
@Override
public String getDisplayGetter() {
return displayName;
}
public void setIconName(String value) {
iconName = value;
icon = null;
}
public void setIcon(Icon value) {
iconName = "";
icon = value;
}
@Override
public final void paintIcon(ComponentDrawContext context,
int x, int y, AttributeSet attrs) {
InstancePainter painter = context.getInstancePainter();
painter.setFactory(this, attrs);
Graphics g = painter.getGraphics();
g.translate(x, y);
paintIcon(painter);
g.translate(-x, -y);
if (painter.getFactory() == null) {
Icon i = icon;
if (i == null) {
String n = iconName;
if (n != null) {
i = Icons.getIcon(n);
if (i == null) {
n = null;
}
}
}
if (i != null) {
i.paintIcon(context.getDestination(), g, x + 2, y + 2);
} else {
super.paintIcon(context, x, y, attrs);
}
}
}
@Override
public final Component createComponent(Location loc, AttributeSet attrs) {
InstanceComponent ret = new InstanceComponent(this, loc, attrs);
configureNewInstance(ret.getInstance());
return ret;
}
public void setOffsetBounds(Bounds value) {
bounds = value;
}
@Override
public Bounds getOffsetBounds(AttributeSet attrs) {
Bounds ret = bounds;
if (ret == null) {
throw new RuntimeException("offset bounds unknown: "
+ "use setOffsetBounds or override getOffsetBounds");
}
return ret;
}
public boolean contains(Location loc, AttributeSet attrs) {
Bounds bds = getOffsetBounds(attrs);
if (bds == null) {
return false;
}
return bds.contains(loc, 1);
}
public Attribute<Direction> getFacingAttribute() {
return facingAttribute;
}
public void setFacingAttribute(Attribute<Direction> value) {
facingAttribute = value;
}
public KeyConfigurator getKeyConfigurator() {
return keyConfigurator;
}
public void setKeyConfigurator(KeyConfigurator value) {
keyConfigurator = value;
}
public void setAttributes(Attribute<?>[] attrs, Object[] defaults) {
this.attrs = attrs;
this.defaults = defaults;
}
@Override
public AttributeSet createAttributeSet() {
Attribute<?>[] as = attrs;
AttributeSet ret = as == null ? AttributeSets.EMPTY : AttributeSets.fixedSet(as, defaults);
return ret;
}
@Override
public Object getDefaultAttributeValue(Attribute<?> attr, LogisimVersion ver) {
Attribute<?>[] as = attrs;
if (as != null) {
for (int i = 0; i < as.length; i++) {
if (as[i] == attr) {
return defaults[i];
}
}
return null;
} else {
AttributeSet dfltSet = defaultSet;
if (dfltSet == null) {
dfltSet = createAttributeSet();
defaultSet = dfltSet;
}
return dfltSet.getValue(attr);
}
}
public void setPorts(Port[] ports) {
portList = UnmodifiableList.decorate(Arrays.asList(ports));
}
public void setPorts(List<Port> ports) {
portList = Collections.unmodifiableList(ports);
}
public List<Port> getPorts() {
return portList;
}
public void setDefaultToolTip(String value) {
defaultToolTip = value;
}
public String getDefaultToolTip() {
return defaultToolTip;
}
public void setInstancePoker(Class<? extends InstancePoker> pokerClass) {
if (isClassOk(pokerClass, InstancePoker.class)) {
this.pokerClass = pokerClass;
}
}
public void setInstanceLogger(Class<? extends InstanceLogger> loggerClass) {
if (isClassOk(loggerClass, InstanceLogger.class)) {
this.loggerClass = loggerClass;
}
}
public void setShouldSnap(boolean value) {
shouldSnap = Boolean.valueOf(value);
}
private boolean isClassOk(Class<?> sub, Class<?> sup) {
boolean isSub = sup.isAssignableFrom(sub);
if (!isSub) {
//OK
System.err.println(sub.getName() + " must be a subclass of " + sup.getName());
return false;
}
try {
sub.getConstructor(new Class[0]);
return true;
} catch (SecurityException e) {
//OK
System.err.println(sub.getName() + " needs its no-args constructor to be public");
} catch (NoSuchMethodException e) {
//OK
System.err.println(sub.getName() + " is missing a no-arguments constructor");
}
return true;
}
@Override
public final Object getFeature(Object key, AttributeSet attrs) {
if (key == FACING_ATTRIBUTE_KEY) {
return facingAttribute;
}
if (key == KeyConfigurator.class) {
return keyConfigurator;
}
if (key == SHOULD_SNAP) {
return shouldSnap;
}
return super.getFeature(key, attrs);
}
@Override
public final void drawGhost(ComponentDrawContext context, Color color,
int x, int y, AttributeSet attrs) {
InstancePainter painter = context.getInstancePainter();
Graphics g = painter.getGraphics();
g.setColor(color);
g.translate(x, y);
painter.setFactory(this, attrs);
paintGhost(painter);
g.translate(-x, -y);
if (painter.getFactory() == null) {
super.drawGhost(context, color, x, y, attrs);
}
}
public void paintIcon(InstancePainter painter) {
painter.setFactory(null, null);
}
public void paintGhost(InstancePainter painter) {
painter.setFactory(null, null);
}
public abstract void paintInstance(InstancePainter painter);
public abstract void propagate(InstanceState state);
// event methods
protected void configureNewInstance(Instance instance) { }
protected void instanceAttributeChanged(Instance instance, Attribute<?> attr) { }
protected Object getInstanceFeature(Instance instance, Object key) {
if (key == Pokable.class && pokerClass != null) {
return new InstancePokerAdapter(instance.getComponent(), pokerClass);
} else if (key == Loggable.class && loggerClass != null) {
return new InstanceLoggerAdapter(instance.getComponent(), loggerClass);
} else {
return null;
}
}
public final InstanceState createInstanceState(CircuitState state, Instance instance) {
return new InstanceStateImpl(state, instance.getComponent());
}
public final InstanceState createInstanceState(CircuitState state, Component comp) {
return createInstanceState(state, ((InstanceComponent) comp).getInstance());
}
}