components/visual-panels/core/src/java/vpanels/panel/com/oracle/solaris/vp/panel/common/action/StructuredAction.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/visual-panels/core/src/java/vpanels/panel/com/oracle/solaris/vp/panel/common/action/StructuredAction.java Thu May 24 04:16:47 2012 -0400
@@ -0,0 +1,327 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ */
+
+package com.oracle.solaris.vp.panel.common.action;
+
+import java.awt.event.ActionEvent;
+import java.util.concurrent.*;
+import javax.swing.*;
+import com.oracle.solaris.vp.util.misc.*;
+
+/**
+ * The {@code StructuredAction} encapsulates an action that {@link #work
+ * produces} an output ({@code O}) based optionally on {@link #getPresetInput
+ * preset} ({@code P}) and {@link #getRuntimeInput runtime} ({@code I}) input.
+ * <p/>
+ * Invocation flow is as follows:
+ * <p/>
+ * <table>
+ * <tr><td align='center' colspan='3'>{@link #actionPerformed}</td></tr>
+ * <tr><td align='center' colspan='3'>↓</td></tr>
+ * <tr><td align='center' colspan='3'>{@link #asyncInvoke}</td></tr>
+ * <tr><td align='center' colspan='3'>↓</td></tr>
+ * <tr>
+ * <td align='center' colspan='3'>{@link #invoke(Object) invoke(I)}</td>
+ * </tr>
+ * <tr><td align='center' colspan='3'>↓</td></tr>
+ * <tr valign='top'>
+ * <td>
+ * <table>
+ * <tr><td align='center'>↓↑</td></tr>
+ * <tr><td align='center'>{@link #getPresetInput}</td></tr>
+ * </table>
+ * </td>
+ * <td>
+ * <table>
+ * <tr><td align='center'>↓↑</td></tr>
+ * <tr><td align='center'>{@link #getRuntimeInput}</td></tr>
+ * </table>
+ * </td>
+ * <td>
+ * <table>
+ * <tr><td align='center'>↓</td></tr>
+ * <tr><td align='center'>{@link #work}</td></tr>
+ * </table>
+ * </td>
+ * </tr>
+ * </table>
+ * <p/>
+ * See the summaries of each to for a description of the features that each
+ * method provides.
+ * <p/>
+ * A typical subclass will implement {@link #work}, and optionally {@link
+ * #getRuntimeInput} if it needs to create or retrieve run-time data (like user
+ * input) prior to running.
+ * <p/>
+ * Simpler implementations that don't require runtime input may override {@link
+ * #invoke(Object) invoke(I)} instead.
+ */
+public class StructuredAction<P, I, O> extends AbstractAction {
+ //
+ // Instance data
+ //
+
+ private boolean loops;
+ private P pInput;
+
+ // Use a thread pool to invoke actions outside of the AWT event thread, and
+ // to autmatically handle uncaught exceptions and queued requests
+ protected final ThreadPoolExecutor threadPool;
+ {
+ String name = TextUtil.getBaseName(getClass()) + "-";
+ ThreadFactory factory = new NamedThreadFactory(name);
+
+ // Unbounded
+ BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
+
+ // Use a thread pool with a single core thread to automatically handle
+ // uncaught exceptions and queued requests. Use a minuscule timeout so
+ // that threads herein don't hold up shutdown of the JVM.
+ threadPool = new ThreadPoolExecutor(
+ 1, 1, 1, TimeUnit.NANOSECONDS, queue, factory);
+
+ threadPool.allowCoreThreadTimeOut(true);
+ }
+
+ //
+ // Constructors
+ //
+
+ /**
+ * Constructs a {@code StructuredAction} with the given name and initial
+ * preset input.
+ */
+ public StructuredAction(String text, P pInput) {
+ putValue(Action.NAME, text);
+ setPresetInput(pInput);
+ }
+
+ /**
+ * Constructs a {@code StructuredAction} with the given name {@code null}
+ * preset input.
+ */
+ public StructuredAction(String text) {
+ this(text, null);
+ }
+
+ //
+ // Action methods
+ //
+
+ /**
+ * Calls {@link #asyncInvoke}.
+ */
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ asyncInvoke();
+ }
+
+ //
+ // StructuredAction methods
+ //
+
+ /**
+ * Runs the given {@code Runnable} in this {@code
+ * StructuredAction}'s thread pool.
+ *
+ * @param r
+ * the {@code Runnable} to run
+ */
+ public void asyncExec(Runnable r) {
+ threadPool.execute(r);
+ }
+
+ /**
+ * Schedules a call to {@link #invoke(Object) invoke(I)} in this
+ * {@code StructuredAction}'s thread pool, ignoring any resulting
+ * {@link ActionAbortedException} or {@link ActionFailedException}.
+ */
+ public void asyncInvoke() {
+ asyncInvoke(null);
+ }
+
+ protected void asyncInvoke(final I rtInput) {
+ asyncExec(
+ new Runnable() {
+ @Override
+ public void run() {
+ try {
+ invoke(rtInput);
+ } catch (ActionAbortedException ignore) {
+ } catch (ActionFailedException ignore) {
+ }
+ }
+ });
+ }
+
+ /**
+ * Gets whether this action loops repeatedly until cancelled or successful.
+ * See {@link #invoke(Object) invoke(I)}.
+ * <p\>
+ * The default value is {@code false}.
+ */
+ public boolean getLoops() {
+ return loops;
+ }
+
+ /**
+ * Gets the preset input for this action.
+ */
+ public P getPresetInput() {
+ return pInput;
+ }
+
+ /**
+ * Retrieves any runtime data needed to perform this action. In most cases
+ * this input will come from the user. This object is passed to {@link
+ * #work} and its ilk.
+ * <p\>
+ * This default implementation does nothing and returns {@code null}.
+ *
+ * @param pInput
+ * the preset input, set prior to the invocation of this action
+ *
+ * @param rtInput
+ * the result from any previous invocations of this method
+ * (from the context of a single invocation of {@link
+ * #invoke}), or {@code null} if this method has not been
+ * called yet
+ *
+ * @exception ActionAbortedException
+ * if this action is cancelled, presumably by the user
+ */
+ public I getRuntimeInput(P pInput, I rtInput)
+ throws ActionAbortedException {
+
+ return null;
+ }
+
+ /**
+ * Calls {@link #invoke(Object) invoke(null)}.
+ */
+ public O invoke() throws ActionAbortedException, ActionFailedException {
+ return invoke(null);
+ }
+
+ /**
+ * Invokes this {@link StructuredAction} with the given initial input. The
+ * behavior of this method when this {@link StructuredAction} is not enabled
+ * may be undefined.
+ * <p/>
+ * This implementation retrieves input from {@link #getRuntimeInput}, then
+ * passes it on to {@link #work}.
+ * <p/>
+ * If the latter throws an {@link ActionFailedException} and {@link
+ * #getLoops} returns:
+ * <ul>
+ * <li>
+ * {@code true}: this process is repeated until cancelled via (an
+ * {@link ActionAbortedException}) or successful
+ * </li>
+ * <li>
+ * {@code false}: the {@link ActionFailedException} is rethrown
+ * </li>
+ * </ul>
+ *
+ * @param rtInput
+ * an initial runtime input
+ *
+ * @return the value returned by {@link #work}
+ *
+ * @exception ActionAbortedException
+ * thrown by {@link #getRuntimeInput} or {@link #work}
+ *
+ * @exception ActionFailedException
+ * thrown by {@link #work}
+ */
+ protected O invoke(I rtInput)
+ throws ActionAbortedException, ActionFailedException {
+
+ P pInput = this.pInput;
+
+ while (true) {
+ rtInput = getRuntimeInput(pInput, rtInput);
+
+ try {
+ return work(pInput, rtInput);
+ } catch (ActionFailedException e) {
+ if (!getLoops()) {
+ throw e;
+ }
+ }
+ }
+ }
+
+ /**
+ * Updates this action's properties based on the preset input or other
+ * criteria. This default implementation does nothing.
+ */
+ public void refresh() {
+ }
+
+ /**
+ * Sets whether this action loops repeatedly until cancelled or successful.
+ * See {@link #invoke(Object) invoke(I)}.
+ * <p\>
+ * The default value is {@code false}.
+ */
+ protected void setLoops(boolean loops) {
+ this.loops = loops;
+ }
+
+ /**
+ * Sets the preset input for this action. This value is passed by {@code
+ * #invoke(Object) invoke(I)} to {@link #getRuntimeInput} and {@link #work}.
+ */
+ public void setPresetInput(P pInput) {
+ this.pInput = pInput;
+ refresh();
+ }
+
+ /**
+ * Does the core work of this action.
+ * <p\>
+ * This default implementation does nothing and returns {@code null}.
+ *
+ * @param pInput
+ * the preset input, set prior to the invocation of this action
+ *
+ * @param rtInput
+ * data retrieved from {@link #getRuntimeInput}
+ *
+ * @return the output of this action, if any
+ *
+ * @exception ActionAbortedException
+ * if the action is cancelled
+ *
+ * @exception ActionFailedException
+ * if an error occurs
+ */
+ public O work(P pInput, I rtInput) throws ActionAbortedException,
+ ActionFailedException {
+
+ return null;
+ }
+}