/*
* (c) Rob Gordon 2005.
*/
package org.oddjob.designer.components;
import java.awt.Component;
import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;
import org.oddjob.arooa.ArooaConstants;
import org.oddjob.arooa.ArooaException;
import org.oddjob.arooa.ArooaParseException;
import org.oddjob.arooa.ArooaSession;
import org.oddjob.arooa.ArooaType;
import org.oddjob.arooa.ArooaValue;
import org.oddjob.arooa.ConfigurationHandle;
import org.oddjob.arooa.ElementMappings;
import org.oddjob.arooa.design.DesignComponent;
import org.oddjob.arooa.design.DesignElementProperty;
import org.oddjob.arooa.design.DesignFactory;
import org.oddjob.arooa.design.DesignInstance;
import org.oddjob.arooa.design.DesignListener;
import org.oddjob.arooa.design.DesignStructureEvent;
import org.oddjob.arooa.design.InstanceSupport;
import org.oddjob.arooa.design.SimpleDesignProperty;
import org.oddjob.arooa.design.screem.BorderedGroup;
import org.oddjob.arooa.design.screem.Form;
import org.oddjob.arooa.design.screem.FormItem;
import org.oddjob.arooa.design.screem.StandardForm;
import org.oddjob.arooa.design.view.DesignViewException;
import org.oddjob.arooa.design.view.SwingFormFactory;
import org.oddjob.arooa.design.view.SwingItemFactory;
import org.oddjob.arooa.design.view.SwingItemView;
import org.oddjob.arooa.design.view.multitype.AbstractMultiTypeModel;
import org.oddjob.arooa.design.view.multitype.EditableValue;
import org.oddjob.arooa.design.view.multitype.MultiTypeRow;
import org.oddjob.arooa.design.view.multitype.MultiTypeStrategy;
import org.oddjob.arooa.design.view.multitype.MultiTypeTableWidget;
import org.oddjob.arooa.life.InstantiationContext;
import org.oddjob.arooa.life.SimpleArooaClass;
import org.oddjob.arooa.parsing.AbstractConfigurationNode;
import org.oddjob.arooa.parsing.ArooaContext;
import org.oddjob.arooa.parsing.ArooaElement;
import org.oddjob.arooa.parsing.ArooaHandler;
import org.oddjob.arooa.parsing.CutAndPasteSupport;
import org.oddjob.arooa.parsing.PrefixMappings;
import org.oddjob.arooa.parsing.QTag;
import org.oddjob.arooa.reflect.ArooaClass;
import org.oddjob.arooa.runtime.AbstractRuntimeConfiguration;
import org.oddjob.arooa.runtime.ConfigurationNode;
import org.oddjob.arooa.runtime.RuntimeConfiguration;
import org.oddjob.values.VariablesJob;
/**
* A {@link DesignFactory} for {@link VariablesJob}
*/
public class VariablesDC implements DesignFactory {
public DesignInstance createDesign(ArooaElement element,
ArooaContext parentContext) {
return new VariablesDesign(element, parentContext);
}
}
class VariablesDesign implements DesignComponent {
final List<PropertyValuePair> properties =
new ArrayList<PropertyValuePair>();
private String id;
private ArooaContext context;
private ArooaElement element;
private List<VariablesListener> listeners =
new ArrayList<VariablesListener>();
public VariablesDesign(ArooaElement element, ArooaContext parentContext) {
this.element = element;
this.id = element.getAttributes().get(ArooaConstants.ID_PROPERTY);
this.context = new VariablesDesignContext(this, parentContext);
}
public ArooaElement element() {
return element;
}
public Form detail() {
return new StandardForm(this)
.addFormItem(new BorderedGroup("Variables")
.add(new VariablesGrid(this)));
}
@Override
public String getId() {
return id;
}
@Override
public void setId(String id) {
this.id = id;
}
public void addStructuralListener(DesignListener listener) {
// No child components so nothing to do here.
}
public void removeStructuralListener(DesignListener listener) {
}
public ArooaContext getArooaContext() {
return context;
}
@Override
public String toString() {
if (id == null) {
return "VariablesJob";
}
return id;
}
void addVariablesListener(VariablesListener listener) {
synchronized (listeners) {
for (int i = 0; i < properties.size(); ++i) {
listener.variableAdded(i);
}
listeners.add(listener);
}
}
void addProperty(int index, VariablesDesignProperty property) {
synchronized (listeners) {
properties.add(index, new PropertyValuePair(property));
for (VariablesListener listener: listeners) {
listener.variableAdded(index);
}
}
}
void removeProperty(int index) {
synchronized (listeners) {
properties.remove(index);
for (VariablesListener listener: listeners) {
listener.variableRemoved(index);
}
}
}
DesignInstance instanceAt(int index) {
return properties.get(index).getValue();
}
VariablesDesignProperty propertyAt(int index) {
return properties.get(index).getProperty();
}
int propertyCount() {
return properties.size();
}
}
/**
* Keeps track of the property and its instance.
*/
class PropertyValuePair {
private final VariablesDesignProperty property;
private DesignInstance value;
PropertyValuePair(VariablesDesignProperty property) {
this.property = property;
property.addDesignListener(new DesignListener() {
public void childAdded(DesignStructureEvent event) {
value = event.getChild();
}
public void childRemoved(DesignStructureEvent event) {
value = null;
}
});
}
VariablesDesignProperty getProperty() {
return property;
}
DesignInstance getValue() {
return value;
}
}
/**
* Receive notifications of when variables are added and removed.
*/
interface VariablesListener {
void variableAdded(int index);
void variableRemoved(int index);
}
/**
*
*/
class VariablesDesignProperty extends SimpleDesignProperty {
private String property;
public VariablesDesignProperty(String property,
Class<?> propertyClass,
ArooaType type, DesignInstance parent) {
super(null, propertyClass, type, parent);
this.property = property;
}
@Override
public String property() {
return property;
}
void changePropertyName(String name) {
this.property = name;
}
}
/**
* Handles parsing of the design etc.
*/
class VariablesDesignContext implements ArooaContext {
private final ArooaContext parent;
private final VariablesDesign variables;
private final ConfigurationNode configurationNode = new AbstractConfigurationNode() {
public ArooaContext getContext() {
return VariablesDesignContext.this;
}
public void addText(String text) {
String trimmedText = text.trim();
if (trimmedText.length() > 0) {
throw new ArooaException("No text expected: " + trimmedText);
}
}
public ConfigurationHandle parse(ArooaContext parentContext)
throws ArooaParseException {
ArooaElement element = new ArooaElement(
variables.element().getUri(),
variables.element().getTag());
String id = variables.getId();
if (id != null && id.length() > 0) {
element = element.addAttribute(
ArooaConstants.ID_PROPERTY, id);
}
ArooaContext nextContext = parentContext.getArooaHandler().onStartElement(
element, parentContext);
for (int i = 0; i < variables.propertyCount(); ++i) {
DesignElementProperty property = variables.propertyAt(i);
property.getArooaContext().getConfigurationNode().parse(nextContext);
}
int index = parentContext.getConfigurationNode().insertChild(
nextContext.getConfigurationNode());
try {
nextContext.getRuntime().init();
}
catch (RuntimeException e) {
parentContext.getConfigurationNode().removeChild(index);
throw e;
}
return new ChainingConfigurationHandle(
getContext(), parentContext, index);
}
};
private final RuntimeConfiguration runtime = new AbstractRuntimeConfiguration() {
public void init() {
fireBeforeInit();
RuntimeConfiguration parentRuntime = parent.getRuntime();
// check it's not the root
if (parentRuntime != null) {
int index = parent.getConfigurationNode().indexOf(
configurationNode);
if (index < 0) {
throw new IllegalStateException(
"Configuration node not added to parent.");
}
parentRuntime.setIndexedProperty(null, index, variables);
}
fireAfterInit();
}
public void configure() {
fireBeforeConfigure();
fireAfterConfigure();
}
public void destroy() {
fireBeforeDestroy();
RuntimeConfiguration parentRuntime = parent.getRuntime();
// check it's not the root
if (parentRuntime != null) {
int index = parent.getConfigurationNode().indexOf(
configurationNode);
if (index < 0) {
throw new IllegalStateException(
"Configuration node not added to parent.");
}
parentRuntime.setIndexedProperty(null, index, null);
}
fireAfterDestroy();
}
public void setProperty(String name, Object value) throws ArooaException {
throw new UnsupportedOperationException("Properties are fixed in Design Mode.");
}
public void setIndexedProperty(String name, int index, Object value)
throws ArooaException {
if (value == null) {
variables.removeProperty(index);
}
else {
variables.addProperty(index, (VariablesDesignProperty) value);
}
}
public void setMappedProperty(String name, String key, Object value)
throws ArooaException {
throw new UnsupportedOperationException("Properties are fixed in Design Mode.");
}
public ArooaClass getClassIdentifier() {
return new SimpleArooaClass(VariablesJob.class);
}
};
VariablesDesignContext(
VariablesDesign variables,
ArooaContext parent) {
this.parent = parent;
this.variables = variables;
}
public ArooaType getArooaType() {
return ArooaType.VALUE;
}
public ArooaContext getParent() {
return parent;
}
public RuntimeConfiguration getRuntime() {
return runtime;
}
public PrefixMappings getPrefixMappings() {
return parent.getPrefixMappings();
}
public ArooaSession getSession() {
return parent.getSession();
}
public ConfigurationNode getConfigurationNode() {
return configurationNode;
}
public ArooaHandler getArooaHandler() {
return new ArooaHandler() {
public ArooaContext onStartElement(ArooaElement element,
ArooaContext parentContext) throws ArooaException {
if (variables.properties.contains(element.getTag())) {
throw new ArooaException("Duplicate property " + element);
}
if (element.getAttributes().getAttributNames().length > 0) {
throw new ArooaException("Property can't contain attributes: " +
element.getAttributes().getAttributNames()[0]);
}
VariablesDesignProperty property = new VariablesDesignProperty(
element.getTag(), Object.class,
ArooaType.VALUE, variables);
return property.getArooaContext();
}
};
}
}
class VariablesGrid implements FormItem {
static {
SwingItemFactory.register(VariablesGrid.class,
new SwingItemFactory<VariablesGrid>() {
public SwingItemView onCreate(VariablesGrid viewModel) {
return new VariablesTableView(viewModel);
}
});
}
private String title;
private final VariablesDesign variables;
public VariablesGrid(VariablesDesign variables) {
this.variables = variables;
}
public VariablesDesign getVariables() {
return variables;
}
public String getTitle() {
return title;
}
public boolean isPopulated() {
return true;
}
public FormItem setTitle(String title) {
this.title = title;
return this;
}
}
class VariablesModel extends AbstractMultiTypeModel {
public static final QTag NULL_TAG = new QTag("");
private final QTag[] supportedTypes;
private final List<VariableRow> variableRows =
new ArrayList<VariableRow>();
private final VariablesDesign variables;
public VariablesModel(VariablesGrid variablesGrid) {
this.variables = variablesGrid.getVariables();
variables.addVariablesListener(new VariablesListener() {
@Override
public void variableAdded(final int index) {
final VariableRow variableRow = new VariableRow(index);
variableRows.add(index, variableRow);
fireRowInserted(index);
variables.propertyAt(index).addDesignListener(new DesignListener() {
public void childAdded(DesignStructureEvent event) {
variableRow.setInstance(event.getChild());
fireRowChanged(index);
}
public void childRemoved(DesignStructureEvent event) {
variableRow.setInstance(null);
fireRowChanged(index);
}
});
}
public void variableRemoved(int index) {
variableRows.remove(index);
fireRowRemoved(index);
}
});
ArooaContext context = variables.getArooaContext();
ElementMappings mappings = context.getSession(
).getArooaDescriptor().getElementMappings();
ArooaSession session = context.getSession();
InstantiationContext instantiationContext =
new InstantiationContext(ArooaType.VALUE,
new SimpleArooaClass(ArooaValue.class),
session.getArooaDescriptor().getClassResolver(),
session.getTools().getArooaConverter());
ArooaElement[] supportedElements =
mappings.elementsFor(instantiationContext);
TreeSet<QTag> sortedTypes = new TreeSet<QTag>();
for (ArooaElement element : supportedElements) {
sortedTypes.add(new QTag(
element, context));
}
this.supportedTypes = sortedTypes.toArray(new QTag[sortedTypes.size()]);
}
@Override
public Object getDeleteOption() {
return NULL_TAG;
}
@Override
public Object[] getTypeOptions() {
return supportedTypes;
}
@Override
public void createRow(Object creator, int row) {
insertProperty(row, (String) creator);
}
@Override
public void swapRow(int from, int direction) {
ArooaContext propertyContext = variables.propertyAt(from).getArooaContext();
ArooaContext parentContext = propertyContext.getParent();
CutAndPasteSupport.cut(parentContext, propertyContext);
int to = from+direction;
try {
CutAndPasteSupport.paste(parentContext, to,
propertyContext.getConfigurationNode());
} catch (ArooaParseException e) {
throw new DesignViewException(e);
}
}
@Override
public void removeRow(int index) {
removeProperty(variables.propertyAt(index));
}
@Override
public MultiTypeRow getRow(int index) {
return variableRows.get(index);
}
@Override
public int getRowCount() {
return variableRows.size();
}
void removeProperty(DesignElementProperty property) {
property.getArooaContext().getRuntime().destroy();
ConfigurationNode configurationNode = variables.getArooaContext().getConfigurationNode();
int index = configurationNode.indexOf(property.getArooaContext().getConfigurationNode());
if (index < 0) {
throw new IllegalStateException("Configuration node is not a child of the variables context.");
}
configurationNode.removeChild(index);
}
/**
*
* @param index
* @param property
*/
void insertProperty(int index, String property) {
variables.getArooaContext().getConfigurationNode().setInsertPosition(index);
ArooaContext nextContext = variables.getArooaContext().getArooaHandler().onStartElement(
new ArooaElement(property), variables.getArooaContext());
variables.getArooaContext().getConfigurationNode().setInsertPosition(index);
int i = variables.getArooaContext().getConfigurationNode().insertChild(
nextContext.getConfigurationNode());
try {
nextContext.getRuntime().init();
}
catch (ArooaException e) {
variables.getArooaContext().getConfigurationNode().removeChild(i);
}
}
/**
*
*/
class VariableRow implements MultiTypeRow {
private final VariablesDesignProperty designProperty;
private DesignInstance instance;
private Component component;
public VariableRow(int index) {
this.designProperty = variables.propertyAt(index);
}
void setInstance(DesignInstance instance) {
this.instance = instance;
if (instance == null) {
component = null;
}
else {
Form designDefintion = instance.detail();
this.component = SwingFormFactory.create(designDefintion).cell();
}
}
@Override
public String getName() {
return designProperty.property();
}
@Override
public void setName(String name) {
designProperty.changePropertyName(name);
}
@Override
public void setType(Object value) {
InstanceSupport support = new InstanceSupport(designProperty);
QTag newType = (QTag) value;
QTag oldType;
if (instance == null) {
oldType = NULL_TAG;
}
else {
oldType = InstanceSupport.tagFor(instance);
}
if (newType.equals(oldType)) {
// if the type hasn't changed then nothing to do.
return;
}
else if (!NULL_TAG.equals(oldType)){
support.removeInstance(instance);
}
if (NULL_TAG.equals(newType)) {
return;
}
try {
support.insertTag(0, newType);
}
catch (ArooaParseException e) {
throw new DesignViewException(e);
}
}
@Override
public Object getType() {
if (instance != null) {
return InstanceSupport.tagFor(instance);
}
else {
return NULL_TAG;
}
}
@Override
public EditableValue getValue() {
if (instance == null) {
return null;
}
else {
return new EditableValue() {
@Override
public Component getEditor() {
return component;
}
@Override
public void commit() {
}
@Override
public void abort() {
}
};
}
}
}
}
class VariablesTableView implements SwingItemView {
private Component component;
public VariablesTableView(VariablesGrid variablesGrid) {
this.component = new MultiTypeTableWidget(
new VariablesModel(variablesGrid),
MultiTypeStrategy.Strategies.NAMED);
}
/* (non-Javadoc)
* @see org.oddjob.designer.view.ViewProducer#inline(java.awt.Container, int, int, boolean)
*/
public int inline(Container container, int row, int column,
boolean selectionInGroup) {
GridBagConstraints c = new GridBagConstraints();
c.weightx = 1.0;
c.weighty = 0.0;
c.fill = GridBagConstraints.HORIZONTAL;
c.anchor = GridBagConstraints.NORTHWEST;
c.gridx = column;
c.gridy = row;
c.gridwidth = GridBagConstraints.REMAINDER;
c.insets = new Insets(3, 3, 3, 3);
container.add(component, c);
return row + 1;
}
/* (non-Javadoc)
* @see org.oddjob.designer.view.ViewProducer#setEnabled(boolean)
*/
public void setEnabled(boolean enabled) {
// Currently not used as the view will not be part of a selection.
component.setEnabled(enabled);
}
}