This tutorial shows how to add an action, invoked by a menu item, for reading values from the grid view selection and calculating averages or sums.
The following sections are covered in this tutorial:
Contents:
- Prerequisite Knowledge
- Exploring the Sample
- Using the IDE's Wizards
- Coding the Module
- Wrapping Up
- Conclusion
This tutorial assumes that you have worked through Getting Started with Extending IJC and that you are familiar with the concepts and instructions described therein. As before, you will use NetBeans IDE to create the sample that will extend IJC. As a result, you are assumed to be in possession of the required software and to know how to use NetBeans IDE to:
No time will be spent on the above topics in this tutorial.
Before we begin creating our module, let's get to know the sample that we will build in this tutorial. The sample is already installed in IJC. Here, we try it out.
Above, the values 169.07 and 202.55 are selected.
Now that you know the functionality that this tutorial provides, let's create the functionality from scratch.
In this section, we use the New Module wizard, described in Getting Started with Extending IJC, to create a source structure.
You now have a source structure for your module.
Our next step is to specify which APIs we want to use. Each API is provided by a module. Some of the APIs belong to the NetBeans APIs, while others belong to the Instant JChem APIs. Because our module is part of the IJC application, which itself makes use of all the NetBeans APIs and IJC APIs that we need, we can set dependencies on the modules that are available to the application.
| Module | Purpose | Classes Relevant to Our Module |
|---|---|---|
|
||
|
|
|
|
||
|
||
|
You should now see the following:
First, let's begin by creating an interface for our computations.
public interface ComputationPerformer {
public void reset();
public void addValue(Object value);
public Object getResult();
public String getResultDisplayMessage();
public String getProgressName();
public Class[] getSupportedFieldCapabilities();
}
Next, we'll implement the interface that we created in the previous section.
import com.im.df.api.capabilities.DFFieldFloatCapability; import com.im.df.api.capabilities.DFFieldIntegerCapability;
public abstract class AbstractComputationPerformer implements ComputationPerformer {
private static final Class[] DEFAULT_SUPPORTED_CAPABILITIES
= new Class[] { DFFieldFloatCapability.class,
DFFieldIntegerCapability.class };
private int counter = 0;
public void reset() {
counter = 0;
}
public int getCount() {
return counter;
}
public void addValue(Object value) {
counter++;
}
public Class[] getSupportedFieldCapabilities() {
return DEFAULT_SUPPORTED_CAPABILITIES;
}
}
We will create an abstract action, which we will use as the basis of both the actions that we create in the following section.
import com.im.df.api.ddl.DFField; import com.im.df.api.dml.DFResultSet; import com.im.df.api.support.DFFeedback; import com.im.df.api.support.SelectionDescription; import com.im.df.api.util.DIFUtilities; import com.im.df.util.UIBackgroundRunnerRO; import com.im.ijc.core.api.actions.AbstractFieldSelectionAwareAction; import com.im.ijc.core.api.util.IJCCoreUtils; import com.im.ijc.core.api.cookie.IJCWidgetCookie; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; import org.openide.nodes.Node; import org.openide.util.NbBundle;
public abstract class AbstractComputationAction extends AbstractFieldSelectionAwareAction {
private static final int BATCH_MAX_SIZE = 200;
private static final MessageFormat PROGRESS_MSG = new MessageFormat(NbBundle.getMessage(AbstractComputationAction.class, "MSG_ProgressDetails"));
private ComputationPerformer computer;
public AbstractComputationAction(ComputationPerformer computer) {
this.computer = computer;
}
protected void performAction(Node[] nodes) {
IJCWidgetCookie ijcwc = findOVC(nodes);
final List<DFField> fields = IJCCoreUtils.computeFields(ijcwc, false, true);
final DFResultSet.VertexState vs = IJCCoreUtils.computeCommonVS(ijcwc);
UIBackgroundRunnerRO runner = new UIBackgroundRunnerRO(getName(), true) {
public void phase1InRequestProcessor() {
computer.reset();
SelectionDescription selection = vs.getSelection();
int total = selection.getSelectedRowsCount();
getEnvironment().getFeedback().switchToDeterminate(total);
List ids = new ArrayList();
for (int i = selection.getMinSelectionIndex(); i <= selection.getMaxSelectionIndex(); i++) {
if (getEnvironment().isCancelled()) {
return;
}
ids.add(vs.getIdAt(i));
if ((ids.size() > BATCH_MAX_SIZE) || (i == selection.getMaxSelectionIndex())) {
Map<Object, Map<String, Object>> data = vs.getData(ids, getEnvironment());
int current = i - selection.getMinSelectionIndex();
getEnvironment().getFeedback().addMessage(DFFeedback.Type.PROGRESS_MESSAGE, PROGRESS_MSG.format(new Object[] { current, total }), null);
getEnvironment().getFeedback().progress(current);
for (Map<String, Object> row: data.values()) {
for (DFField f: fields) {
computer.addValue(row.get(f.getId()));
}
if (getEnvironment().isCancelled()) {
return;
}
}
ids.clear();
}
}
NotifyDescriptor.Message nd = new NotifyDescriptor.Message(computer.getResultDisplayMessage(), NotifyDescriptor.INFORMATION_MESSAGE);
DialogDisplayer.getDefault().notifyLater(nd);
}
};
runner.start();
}
@Override
protected boolean enableAccordingToCurrentSelection(IJCWidgetCookie ijcwc) {
if (super.enableAccordingToCurrentSelection(ijcwc)) {
DFResultSet.VertexState vs = IJCCoreUtils.computeCommonVS(ijcwc);
if (vs == null) {
return false;
}
List<DFField> fields = IJCCoreUtils.computeFields(ijcwc, false, true);
for (DFField f: fields) {
boolean ok = false;
for (Class c: computer.getSupportedFieldCapabilities()) {
if (DIFUtilities.findCapability(f, c) != null) {
ok = true;
break;
}
}
if (!ok) {
return false;
}
}
return true;
}
return false;
}
}
Let's now implement the abstract action from the previous section. Here, we implement it to create an action to compute sums.
import java.text.MessageFormat; import org.openide.util.NbBundle;
public class ComputeSumAction extends AbstractComputationAction {
private static final MessageFormat RESULT_FORMAT = new MessageFormat(NbBundle.getMessage(ComputeSumAction.class, "MSG_SumResult"));
public ComputeSumAction() {
super(new SumComputer());
}
public String getName() {
return NbBundle.getMessage(ComputeSumAction.class, "NAME_Sum");
}
private static class SumComputer extends AbstractComputationPerformer {
private double sum = 0;
@Override
public void reset() {
super.reset();
sum = 0;
}
@Override
public void addValue(Object value) {
super.addValue(value);
Number n = (Number) value;
sum += n.doubleValue();
}
public String getResultDisplayMessage() {
return RESULT_FORMAT.format(new Object[] { getCount(), getResult() } );
}
public Object getResult() {
return sum;
}
public String getProgressName() {
return NbBundle.getMessage(ComputeSumAction.class, "MSG_ProgressComputingSum");
}
}
}
Let's now implement the abstract action from the previous section. Here, we implement it to create an action to compute averages.
import java.text.MessageFormat; import org.openide.util.NbBundle;
public class ComputeAverageAction extends AbstractComputationAction {
private static final MessageFormat RESULT_FORMAT = new MessageFormat(NbBundle.getMessage(ComputeSumAction.class, "MSG_AvgResult"));
public ComputeAverageAction() {
super(new AverageComputer());
}
public String getName() {
return NbBundle.getMessage(ComputeAverageAction.class, "NAME_Avg");
}
private static class AverageComputer extends AbstractComputationPerformer {
private double sum = 0;
@Override
public void reset() {
super.reset();
sum = 0;
}
@Override
public void addValue(Object value) {
super.addValue(value);
Number n = (Number) value;
sum += n.doubleValue();
}
public String getResultDisplayMessage() {
return RESULT_FORMAT.format(new Object[] { getCount(), getResult() } );
}
public Object getResult() {
return sum / getCount();
}
public String getProgressName() {
return NbBundle.getMessage(ComputeAverageAction.class, "MSG_ProgressComputingAvg");
}
}
}
The centralized location of our strings is our resource bundle. Let's put them there, in 'Bundle.properties', in the main package:
MSG_SumResult=The sum of {0} selected values is {1}.
NAME_Sum=Compute sum
MSG_ProgressComputingSum=Computing sum...
MSG_AvgResult=The average of {0} selected values is {1}.
NAME_Avg=Compute average
MSG_ProgressComputingAvg=Computing average...
MSG_ProgressDetails=Counting - row {0} out of {1}
Finally, let's register our actions in the layer file.
<folder name="Actions">
<folder name="Tools">
<file name="com-im-ijc-mathcomputations-ComputeAverageAction.instance"/>
<file name="com-im-ijc-mathcomputations-ComputeSumAction.instance"/>
</folder>
</folder>
<folder name="Menu">
<folder name="Tools">
<folder name="IJC API examples">
<file name="com-im-ijc-mathcomputations-ComputeSumAction.shadow">
<attr name="originalFile" stringvalue="Actions/Tools/com-im-ijc-mathcomputations-ComputeSumAction.instance"/>
<attr name="position" intvalue="100"/>
</file>
<file name="com-im-ijc-mathcomputations-ComputeAverageAction.shadow">
<attr name="originalFile" stringvalue="Actions/Tools/com-im-ijc-mathcomputations-ComputeAverageAction.instance"/>
<attr name="position" intvalue="200"/>
</file>
</folder>
</folder>
</folder>
Congratulations, you have created actions for computing sums and averages in IJC. You simply need to run the module, as described earlier, and then IJC is extended with your new feature and you can use it and distribute it to your users.