--- a/api.intent/arch.xml
+++ a/api.intent/arch.xml
@@ -0,0 +1,1176 @@
+
+
+]>
+
+
+
+ &api-questions;
+
+
+
+
+ Intent is an description of some intended operation. It is specified
+ by action type (String) and a URI. When some Intent is executed,
+ proper handler is chosen and it performs the actual operation.
+
+
+ Intents provide loose coupling between modules that may request an
+ operation and modules that can perform it. They can be easily
+ serialized, so they are suitable for distributed heterogenous
+ systems.
+
+
+
+
+ API for describing and executing intended operations.
+
+
+
+
+ SPI for handlers that are able to invoke proper operation for
+ specified intents.
+
+
+
+
+
+
+
+
+
+ The code is checked by unit tests.
+
+
+
+
+
+
+
+
+ Done.
+
+
+
+
+
+
+
+
+
+ Future<Object> result = new Intent (Intent.ACTION_VIEW, new URI("scheme://path/")).execute();
+ Object value = result.get();
+
+
+
+
+
+ new Intent (Intent.ACTION_VIEW, new URI("scheme://path/")).execute(new Callback () {
+ void success(Object result) {
+ // use the result somehow
+ }
+ void failure(Exception exception) {
+ // report the failure somehow
+ }
+ });
+
+
+
+
+
+ @IntentHandlerRegistration (
+ displayName = "Show my item in MyEditor",
+ position = 800,
+ uriPattern = "myscheme://.*",
+ actions = {Intent.ACTION_VIEW, Intent.ACTION_EDIT}
+ )
+ public static Object handleIntent(Intent intent) {
+ SomeType result = parseAndPerformIntentSomehow(intent);
+ return result;
+ }
+
+
+
+
+
+ @IntentHandlerRegistration (
+ displayName = "Show my item in MyEditor",
+ position = 800,
+ uriPattern = "myscheme://.*",
+ actions = "*"
+ )
+ public static void handleIntent(final Intent intent, final Result result) {
+ // Move the execution to another thread. Do not wait for the result
+ // here, just pass the result object.
+ EventQueue.invokeLater(new Runnable() {
+ public void run() {
+ try {
+ Object result = doSomethingInEDT(intent);
+ result.setResult(e);
+ } catch (Exception e) {
+ result.setException(e);
+ }
+ }
+ });
+ }
+
+
+
+
+
+
+
+
+
+ This module provides a contract between API clients that can express
+ some intention to invoke an operation and SPI providers that can
+ handle that intention.
+
+
+ This is useful in client-server environments, where the intention
+ can be constructed on server-side, but handled on client-side. The
+ objects that describe the intention should be easy to construct,
+ transfer and interpret.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No deprecation needed.
+
+
+
+
+
+
+
+
+ Yes.
+
+
+
+
+
+
+
+
+ No standards.
+
+
+
+
+
+
+
+
+ No settings are read or written.
+
+
+
+
+
+
+
+
+ 1.6
+
+
+
+
+
+
+
+
+ JRE
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No non-NB dependencies.
+
+
+
+
+
+
+
+
+ Any platform.
+
+
+
+
+
+
+
+
+ Standard module dependency is sufficient.
+
+
+
+
+
+
+
+
+ Just module JAR.
+
+
+
+
+
+
+
+
+ Yes.
+
+
+
+
+
+
+
+
+ Only API and SPI packages are public.
+
+
+
+
+
+
+
+
+ Installation location does not matter.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ The API is threadsafe. SPI implementations should ensure proper
+ synchronization.
+
+
+
+
+
+
+
+
+ No clipboard access.
+
+
+
+
+
+
+
+
+ No Drag & Drop support.
+
+
+
+
+
+
+
+
+ No files are read or written by this module.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ The annotation processor is registered using ServiceProvider.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ Very little memory consumed.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ Code in the module is very simple. Performance is incluenced mosly
+ by SPI implementations, which should run quite quickly.
+
+
+
+
+
+
+
+
+ No performance criteria are enforced. The plugged-in code is invoked
+ by a dedicated executor.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
--- a/api.intent/build.xml
+++ a/api.intent/build.xml
@@ -0,0 +1,5 @@
+
+
+ Builds, tests, and runs the project org.netbeans.api.intent
+
+
--- a/api.intent/manifest.mf
+++ a/api.intent/manifest.mf
@@ -0,0 +1,6 @@
+Manifest-Version: 1.0
+AutoUpdate-Show-In-Client: true
+OpenIDE-Module: org.netbeans.api.intent
+OpenIDE-Module-Localizing-Bundle: org/netbeans/api/intent/Bundle.properties
+OpenIDE-Module-Specification-Version: 1.0
+
--- a/api.intent/nbproject/project.properties
+++ a/api.intent/nbproject/project.properties
@@ -0,0 +1,4 @@
+is.autoload=true
+javac.source=1.7
+javac.compilerargs=-Xlint -Xlint:-serial
+javadoc.arch=${basedir}/arch.xml
--- a/api.intent/nbproject/project.xml
+++ a/api.intent/nbproject/project.xml
@@ -0,0 +1,61 @@
+
+
+ org.netbeans.modules.apisupport.project
+
+
+ org.netbeans.api.intent
+
+
+ org.netbeans.api.annotations.common
+
+
+
+ 1
+ 1.25
+
+
+
+ org.openide.filesystems
+
+
+
+ 9.1
+
+
+
+ org.openide.util
+
+
+
+ 9.3
+
+
+
+ org.openide.util.lookup
+
+
+
+ 8.26
+
+
+
+
+
+ unit
+
+ org.netbeans.libs.junit4
+
+
+
+ org.netbeans.modules.nbjunit
+
+
+
+
+
+ org.netbeans.api.intent
+ org.netbeans.spi.intent
+
+
+
+
--- a/api.intent/src/org/netbeans/api/intent/Bundle.properties
+++ a/api.intent/src/org/netbeans/api/intent/Bundle.properties
@@ -0,0 +1,7 @@
+OpenIDE-Module-Display-Category=Infrastructure
+OpenIDE-Module-Long-Description=\
+ API for invoking intended operations (described by suitable type of object, e.g. \
+ URI) by some of registered handlers.\n\
+ SPI for handlers of possible intended operations.
+OpenIDE-Module-Name=Intent API
+OpenIDE-Module-Short-Description=Performing intended operations by suitable handlers
--- a/api.intent/src/org/netbeans/api/intent/Callback.java
+++ a/api.intent/src/org/netbeans/api/intent/Callback.java
@@ -0,0 +1,72 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2014 Sun Microsystems, Inc.
+ */
+package org.netbeans.api.intent;
+
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+
+/**
+ * Callback invoked when an intent action has finished. It is run in a dedicated
+ * thread in background.
+ *
+ * @see Intent#execute(org.netbeans.api.intent.Callback)
+ *
+ * @author jhavlin
+ */
+public interface Callback {
+
+ /**
+ * Invoked when the intent action has completed successfully. The type of
+ * result depends on implementation of chosen intent handler, it can be
+ * null.
+ *
+ * @param result Result value.
+ */
+ public void success(@NullAllowed Object result);
+
+ /**
+ * Invoked when the intent action has failed.
+ *
+ * @param exception Encountered exception.
+ */
+ public void failure(@NonNull Exception exception);
+}
--- a/api.intent/src/org/netbeans/api/intent/Intent.java
+++ a/api.intent/src/org/netbeans/api/intent/Intent.java
@@ -0,0 +1,290 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2014 Sun Microsystems, Inc.
+ */
+package org.netbeans.api.intent;
+
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.netbeans.modules.intent.CallbackResult;
+import org.netbeans.modules.intent.IntentHandler;
+import org.netbeans.modules.intent.SettableResult;
+import org.netbeans.spi.intent.Result;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.util.Parameters;
+
+/**
+ * Description of some intended operation. The operation is described by an
+ * action and a URI.
+ *
+ *
+ * If the intent is executed, proper registered handler is chosen to perform the
+ * actual operation.
+ *
+ *
+ * For example, the following code can be used to open a file in editor (if the
+ * environment is suitable for such operation).
+ *
+ *
+ * new Intent(Intent.ACTION_VIEW, new URI("file://path/file.txt")).execute();
+ *
+ *
+ * @author jhavlin
+ */
+public final class Intent {
+
+ private static final Logger LOG = Logger.getLogger(Intent.class.getName());
+
+ /**
+ * Standard VIEW action type.
+ */
+ public static final String ACTION_VIEW = "VIEW"; //NOI18N
+
+ /**
+ * Standard EDIT action type.
+ */
+ public static final String ACTION_EDIT = "EDIT"; //NOI18N
+
+ private final String action;
+ private final URI uri;
+
+ /**
+ * Constructor for an intended operation.
+ *
+ * @param action Action type to perform. It is recommended to use either
+ * standard actions predefined in Intent class (see {@link #ACTION_EDIT},
+ * {@link #ACTION_VIEW}), or strings similar to fully qualified field names
+ * (e.g. "org.some.package.ClassName.ACTION_CUSTOM").
+ *
+ * @param uri URI specifying the operation.
+ */
+ public Intent(@NonNull String action, @NonNull URI uri) {
+ Parameters.notNull("action", action);
+ Parameters.notNull("uri", uri);
+ this.action = action;
+ this.uri = uri;
+ }
+
+ /**
+ * Get action type.
+ *
+ * @return The action type.
+ */
+ public @NonNull String getAction() {
+ return action;
+ }
+
+ /**
+ * Get URI specifying this intent.
+ *
+ * @return The URI.
+ */
+ public @NonNull URI getUri() {
+ return uri;
+ }
+
+ /**
+ * Execute the intent. The operation will be run asynchronously.
+ *
+ * If the result is ignored, it's recommended to use
+ * {@code intent.execute(null);}
+ *
+ *
+ * @return {@link Future} Future for result of the action. The type of
+ * result depends on implementation of chosen intent handler, it can be
+ * null.
+ */
+ public @NonNull Future execute() {
+ return IntentHandler.RP.submit(new Callable() {
+
+ @Override
+ public Object call() throws Exception {
+ SettableResult sr = new SettableResult();
+ invoke(Intent.this, sr);
+ if (sr.getException() != null) {
+ throw sr.getException();
+ }
+ return sr.getResult();
+ }
+ });
+ }
+
+ /**
+ * Execute the intent. The operation will be run asynchronously.
+ *
+ * @param callback Callback object that will be notified when the execution
+ * completes. If callback is null, the result will be ignored.
+ */
+ public void execute(@NullAllowed final Callback callback) {
+ IntentHandler.RP.post(new Runnable() {
+
+ @Override
+ public void run() {
+ invoke(Intent.this, callback == null
+ ? null
+ : new CallbackResult(callback));
+ }
+ });
+ }
+
+ /**
+ * Get available actions for the intent.
+ *
+ * @return Immutable set of available actions, sorted by priority.
+ */
+ public @NonNull SortedSet extends IntentAction> getIntentActions() {
+ SortedSet intentHandlers = getIntentHandlers(this);
+ SortedSet actions = new TreeSet<>(
+ new Comparator() {
+
+ @Override
+ public int compare(IntentAction o1, IntentAction o2) {
+ return o1.getPosition() - o2.getPosition();
+ }
+ });
+ for (IntentHandler ih : intentHandlers) {
+ actions.add(new IntentAction(this, ih));
+ }
+ return Collections.unmodifiableSortedSet(actions);
+ }
+
+ private static SortedSet getIntentHandlers(
+ Intent intent) {
+
+ FileObject f = FileUtil.getConfigFile("Services/Intent/Handlers");
+ if (f == null) {
+ return null;
+ }
+ SortedSet candidates = new TreeSet<>();
+ for (FileObject fo : f.getChildren()) {
+ if ("instance".equals(fo.getExt())) {
+ Object pattern = fo.getAttribute("uriPattern");
+ Object displayName = fo.getAttribute("displayName");
+ Object position = fo.getAttribute("position");
+ Object actions = fo.getAttribute("actions");
+ if (pattern instanceof String && displayName instanceof String
+ && position instanceof Integer
+ && actions instanceof String) {
+ if (canSupport((String) pattern, (String) actions, intent)) {
+ try {
+ IntentHandler ih = FileUtil.getConfigObject(
+ fo.getPath(), IntentHandler.class);
+ candidates.add(ih);
+ } catch (Exception e) {
+ LOG.log(Level.INFO,
+ "Cannot instantiate handler for " //NOI18N
+ + fo.getPath(), e);
+ }
+ }
+ } else {
+ LOG.log(Level.FINE, "Invalid URI handler {0}", fo.getPath());
+ }
+ }
+ }
+ return candidates;
+ }
+
+ /**
+ * Check whether an intent is supported by a handler specified by a URI
+ * pattern and action list.
+ *
+ * @param uriPattern Pattern for the URI.
+ * @param actions Comma-separated actions.
+ * @param intent Intent to check.
+ * @return True if the intent matches the URI pattern and action list.
+ */
+ private static boolean canSupport(String uriPattern, String actions,
+ Intent intent) {
+ Pattern p = Pattern.compile(uriPattern);
+ if (p.matcher(intent.getUri().toString()).matches()) {
+ if ("*".equals(actions)) {
+ return true;
+ } else {
+ List actionList = Arrays.asList(actions.split(","));
+ return actionList.contains(intent.getAction());
+ }
+ } else {
+ return false;
+ }
+ }
+
+ private static void invoke(Intent intent, Result resultOrNull) {
+
+ Throwable lastException = null;
+ boolean handled = false;
+
+ for (IntentHandler h : getIntentHandlers(intent)) {
+ try {
+ h.handle(intent, resultOrNull);
+ handled = true;
+ break;
+ } catch (Exception e) {
+ lastException = e;
+ LOG.log(Level.WARNING, null, e);
+ }
+ }
+ if (!handled) {
+ if (resultOrNull != null) {
+ resultOrNull.setException(lastException == null
+ ? new NoAvailableHandlerException(intent)
+ : new NoAvailableHandlerException(intent, lastException));
+ }
+ LOG.log(Level.INFO, "Intent {0} cannot be handled", intent);//NOI18N
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Intent{" + "action=" + action + ", uri=" + uri + '}';
+ }
+}
--- a/api.intent/src/org/netbeans/api/intent/IntentAction.java
+++ a/api.intent/src/org/netbeans/api/intent/IntentAction.java
@@ -0,0 +1,134 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2014 Sun Microsystems, Inc.
+ */
+package org.netbeans.api.intent;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.netbeans.modules.intent.CallbackResult;
+import org.netbeans.modules.intent.IntentHandler;
+import org.netbeans.modules.intent.SettableResult;
+import org.netbeans.spi.intent.Result;
+
+/**
+ * Actual action for an Intent. Pair of an Intent and one of its handlers.
+ *
+ * @see Intent#getIntentActions()
+ * @author jhavlin
+ */
+public final class IntentAction {
+
+ private final Intent intent;
+ private final IntentHandler delegate;
+
+ IntentAction(Intent intent, IntentHandler delegate) {
+ this.intent = intent;
+ this.delegate = delegate;
+ }
+
+ int getPosition() {
+ return delegate.getPosition();
+ }
+
+ /**
+ * Execute the intent action. The operation will be run asynchronously.
+ *
+ * @param callback Callback object that will be notified when the execution
+ * completes. If callback is null, the result will be ignored.
+ */
+ public void execute(@NullAllowed final Callback callback) {
+ IntentHandler.RP.post(new Runnable() {
+ @Override
+ public void run() {
+ Result result = callback == null
+ ? null
+ : new CallbackResult(callback);
+ delegate.handle(intent, result);
+ }
+ });
+ }
+
+ /**
+ * Execute the intent action. The operation will be run asynchronously.
+ *
+ * If the result is ignored, it's recommended to use
+ * {@code intentAction.execute(null);}
+ *
+ *
+ * @return Future for result of the action. The type of result depends on
+ * implementation of chosen intent handler, it can be null.
+ */
+ public @NonNull Future execute() {
+
+ return IntentHandler.RP.submit(new Callable() {
+ @Override
+ public Object call() throws Exception {
+ SettableResult result = new SettableResult();
+ delegate.handle(intent, result);
+ if (result.getException() != null) {
+ throw result.getException();
+ }
+ return result.getResult();
+ }
+ });
+ }
+
+ /**
+ * Get display name of this action.
+ *
+ * @return The localized display name.
+ */
+ public @NonNull String getDisplayName() {
+ return delegate.getDisplayName();
+ }
+
+ /**
+ * Get icon of this action.
+ *
+ * @return Some resource identifier, e.g. icon id, path or URI.
+ * Depends on the platform. If not available, empty string is returned.
+ */
+ public @NonNull String getIcon() {
+ return delegate.getIcon();
+ }
+}
--- a/api.intent/src/org/netbeans/api/intent/NoAvailableHandlerException.java
+++ a/api.intent/src/org/netbeans/api/intent/NoAvailableHandlerException.java
@@ -0,0 +1,64 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2014 Sun Microsystems, Inc.
+ */
+package org.netbeans.api.intent;
+
+/**
+ * Exception thrown when no handler is available for the Intent, or when
+ * invocation of all handlers fails. In the latter case, exception thrown by the
+ * last handler is set as the init cause of this exception.
+ *
+ * @author jhavlin
+ */
+public class NoAvailableHandlerException extends Exception {
+
+ public NoAvailableHandlerException(Intent intent) {
+ super(messageForIntent(intent));
+ }
+
+ public NoAvailableHandlerException(Intent intent, Throwable cause) {
+ super(messageForIntent(intent), cause);
+ }
+
+ private static String messageForIntent(Intent intent) {
+ return "No available handler for intent " + intent;
+ }
+}
--- a/api.intent/src/org/netbeans/api/intent/package-info.java
+++ a/api.intent/src/org/netbeans/api/intent/package-info.java
@@ -0,0 +1,72 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2014 Sun Microsystems, Inc.
+ */
+
+/**
+ * API for working with {@link org.netbeans.api.intent.Intent}s, abstract descriptions of intended
+ * operations.
+ *
+ * Intents can be used when we want to perform some standard operation and we
+ * believe that the environment (the IDE, some application) is capable of
+ * finding and choosing correct action for it.
+ *
+ *
+ * The operations are specified as pair of action type and a URI. See example:
+ *
+ *
+ * {@link org.netbeans.api.intent.Intent} i = new {@link org.netbeans.api.intent.Intent}(Intent.ACTION_VIEW, new URI("file://path/file.txt"));
+ *
+ *
+ * We can execute an Intent to let the system choose to most appropriate
+ * action for the intent and invoke it:
+ *
+ *
+ * i.{@link org.netbeans.api.intent.Intent#execute() execute()};
+ *
+ *
+ * Or we can get list of all available actions, display them somehow, and let
+ * the user select one of them:
+ *
+ *
+ * Set<IntentAction> available = i.{@link org.netbeans.api.intent.Intent#getIntentActions() getIntentActions()};
+ *
+ */
+package org.netbeans.api.intent;
--- a/api.intent/src/org/netbeans/modules/intent/CallbackResult.java
+++ a/api.intent/src/org/netbeans/modules/intent/CallbackResult.java
@@ -0,0 +1,88 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2014 Sun Microsystems, Inc.
+ */
+package org.netbeans.modules.intent;
+
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.netbeans.api.intent.Callback;
+import org.netbeans.spi.intent.Result;
+import org.openide.util.Parameters;
+import org.openide.util.RequestProcessor;
+
+/**
+ *
+ * @author jhavlin
+ */
+public class CallbackResult implements Result {
+
+ private static final RequestProcessor RP
+ = new RequestProcessor("Intent Callbacks");
+
+ private final Callback callback;
+
+ public CallbackResult(@NonNull Callback callback) {
+ this.callback = callback;
+ }
+
+ @Override
+ public void setResult(final Object result) {
+ RP.post(new Runnable() {
+
+ @Override
+ public void run() {
+ callback.success(result);
+ }
+ });
+ }
+
+ @Override
+ public void setException(final Exception exception) {
+ Parameters.notNull("exception", exception); //NOI18N
+ RP.post(new Runnable() {
+
+ @Override
+ public void run() {
+ callback.failure(exception);
+ }
+ });
+ }
+}
--- a/api.intent/src/org/netbeans/modules/intent/IntentHandler.java
+++ a/api.intent/src/org/netbeans/modules/intent/IntentHandler.java
@@ -0,0 +1,163 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2014 Sun Microsystems, Inc.
+ */
+package org.netbeans.modules.intent;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import org.netbeans.api.intent.Intent;
+import org.netbeans.spi.intent.Result;
+import org.openide.filesystems.FileObject;
+import org.openide.util.Lookup;
+import org.openide.util.RequestProcessor;
+
+/**
+ *
+ * @author jhavlin
+ */
+public class IntentHandler implements Comparable {
+
+ public static final RequestProcessor RP = new RequestProcessor(
+ IntentHandler.class);
+
+ private static final Result IGNORING_RESULT = new Result() {
+
+ @Override
+ public void setResult(Object result) {
+ }
+
+ @Override
+ public void setException(Exception exception) {
+ }
+ };
+
+ @Override
+ public int compareTo(IntentHandler o) {
+ return this.getPosition() - o.getPosition();
+ }
+
+ private enum Type {
+ RETURN, SETBACK
+ }
+
+ private final String className;
+ private final String methodName;
+ private final String displayName;
+ private final String icon;
+ private final Type type;
+ private final int position;
+
+ public static IntentHandler create(FileObject fo) {
+ String n = fo.getName();
+ int lastDash = n.lastIndexOf('-');
+ if (lastDash <= 0 || lastDash + 1 >= n.length()) {
+ throw new IllegalArgumentException("Invalid handler file"); //NOI18N
+ }
+ String className = n.substring(0, lastDash).replace('-', '.');
+ String methodName = n.substring(lastDash + 1);
+ String displayName = (String) fo.getAttribute("displayName"); //NOI18N
+ String icon = (String) fo.getAttribute("icon"); //NOI18N
+ int position = (Integer) fo.getAttribute("position"); //NOI18N
+ Type type = Type.valueOf((String) fo.getAttribute("type")); //NOI18N
+
+ return new IntentHandler(className, methodName, displayName, icon,
+ type, position);
+ }
+
+ private IntentHandler(String className, String methodName,
+ String displayName, String icon, Type type, int position) {
+
+ this.className = className;
+ this.methodName = methodName;
+ this.displayName = displayName;
+ this.icon = icon;
+ this.type = type;
+ this.position = position;
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public String getIcon() {
+ return icon;
+ }
+
+ public int getPosition() {
+ return position;
+ }
+
+ public boolean isSetBack() {
+ return type == Type.SETBACK;
+ }
+
+ public void handle(Intent intent, Result resultOrNull) {
+
+ Result result = resultOrNull == null ? IGNORING_RESULT : resultOrNull;
+
+ ClassLoader cls = Lookup.getDefault().lookup(ClassLoader.class);
+ if (cls == null) {
+ throw new IllegalStateException("Classloader not found"); //NOI18N
+ } else {
+ try {
+ Class> c = Class.forName(className, true, cls);
+ if (isSetBack()) {
+ Method m = c.getDeclaredMethod(methodName, Intent.class,
+ Result.class);
+ m.invoke(null, intent, result);
+ } else {
+ Method m = c.getDeclaredMethod(methodName, Intent.class);
+ Object res = m.invoke(null, intent);
+ result.setResult(res);
+ }
+ } catch (InvocationTargetException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof Exception) {
+ result.setException((Exception) cause);
+ } else {
+ result.setException(new Exception(cause));
+ }
+ } catch (ReflectiveOperationException e) {
+ result.setException(e);
+ }
+ }
+ }
+}
--- a/api.intent/src/org/netbeans/modules/intent/OpenUriHandlerProcessor.java
+++ a/api.intent/src/org/netbeans/modules/intent/OpenUriHandlerProcessor.java
@@ -0,0 +1,215 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2014 Sun Microsystems, Inc.
+ */
+package org.netbeans.modules.intent;
+
+import java.util.Set;
+import javax.annotation.processing.Processor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.annotation.processing.SupportedSourceVersion;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+import org.netbeans.api.intent.Intent;
+import org.netbeans.spi.intent.IntentHandlerRegistration;
+import org.netbeans.spi.intent.Result;
+import org.openide.filesystems.annotations.LayerBuilder;
+import org.openide.filesystems.annotations.LayerBuilder.File;
+import org.openide.filesystems.annotations.LayerGeneratingProcessor;
+import org.openide.filesystems.annotations.LayerGenerationException;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author jhavlin
+ */
+@ServiceProvider(service = Processor.class)
+@SupportedSourceVersion(SourceVersion.RELEASE_7)
+@SupportedAnnotationTypes("org.netbeans.spi.intent.IntentHandlerRegistration")
+public class OpenUriHandlerProcessor extends LayerGeneratingProcessor {
+
+ @Override
+ protected boolean handleProcess(
+ Set extends TypeElement> annotations,
+ RoundEnvironment roundEnv) throws LayerGenerationException {
+
+ for (Element e : roundEnv.getElementsAnnotatedWith(
+ IntentHandlerRegistration.class)) {
+ IntentHandlerRegistration r = e.getAnnotation(
+ IntentHandlerRegistration.class);
+ registerHandler(e, r);
+ }
+ return true;
+ }
+
+ private static final String SUFFIX = ".instance"; //NOI18N
+
+ private void registerHandler(Element e, IntentHandlerRegistration r)
+ throws LayerGenerationException {
+
+ TypeElement intentTypeElement = getTypeElement(Intent.class);
+ TypeElement objectTypeElement = getTypeElement(Object.class);
+ TypeElement resultTypeElement = getTypeElement(Result.class);
+
+ if (!ElementKind.METHOD.equals(e.getKind())) {
+ throw error(e, "The annotation can be applied only to" //NOI18N
+ + " a method.");//NOI18N
+ }
+ if (!e.getModifiers().contains(Modifier.STATIC)) {
+ throw error(e, "The annotated method must be static."); //NOI18N
+ }
+
+ ExecutableElement ee;
+ if (e instanceof ExecutableElement) {
+ ee = (ExecutableElement) e;
+ } else {
+ throw error(e, "Annotated element must be an " //NOI18N
+ + "ExecutableElement"); //NOI18N
+
+ }
+
+ String type;
+ if (ee.getParameters().size() == 1
+ && hasParameter(ee, 0, intentTypeElement)
+ && hasResultType(ee, objectTypeElement)) {
+ type = "RETURN";
+ } else if (ee.getParameters().size() == 2
+ && hasParameter(ee, 0, intentTypeElement)
+ && hasParameter(ee, 1, resultTypeElement)
+ && hasVoidResultType(ee)) {
+ type = "SETBACK";
+ } else {
+ throw error(e, "The handler method must take a " //NOI18N
+ + "single argument of type " //NOI18N
+ + "org.netbeans.api.intent.Intent and return Object"//NOI18N
+ + "; or take two arguments of types" //NOI18N
+ + "org.netbeans.api.intent.Intent" //NOI18N
+ + "and org.netbeans.spi.intent.Result" //NOI18N
+ + " and return void."); //NOI18N
+ }
+
+ boolean takeAll = false;
+ boolean empty = true;
+ StringBuilder sb = new StringBuilder();
+ for (String action: r.actions()) {
+ if ("*".equals(action)) {
+ takeAll = true;
+ break;
+ } else {
+ if (!empty) {
+ sb.append(',');
+ }
+ sb.append(action);
+ empty = false;
+ }
+ }
+ String actions = takeAll ? "*" : sb.toString();
+
+ final LayerBuilder b = layer(e);
+ File f = b.file("Services/Intent/Handlers/" //NOI18N
+ + getName(e).replace('.', '-') + SUFFIX);
+ f.position(r.position());
+ f.stringvalue("instanceClass", //NOI18N
+ IntentHandler.class.getCanonicalName());
+ f.methodvalue("instanceCreate", IntentHandler.class.getCanonicalName(),
+ "create"); //NOI18N
+ f.bundlevalue("displayName", r.displayName()); //NOI18N
+ f.stringvalue("uriPattern", r.uriPattern()); //NOI18N
+ f.stringvalue("icon", r.icon()); //NOI18N
+ f.stringvalue("type", type); //NOI18N
+ f.stringvalue("actions", actions); //NOI18N
+ f.write();
+ }
+
+ private String getName(Element e) {
+ if (e.getKind().isClass() || e.getKind().isInterface()) {
+ return processingEnv.getElementUtils().getBinaryName(
+ (TypeElement) e).toString();
+ } else if (e.getKind() == ElementKind.PACKAGE) {
+ return e.getSimpleName().toString();
+ } else {
+ return getName(e.getEnclosingElement()) + '.' + e.getSimpleName();
+ }
+ }
+
+ private boolean hasParameter(ExecutableElement ee, int pos,
+ Element typeElement) {
+ return processingEnv.getTypeUtils().asElement(
+ ee.getParameters().get(pos).asType()).equals(
+ typeElement);
+ }
+
+ private boolean hasVoidResultType(ExecutableElement ee) {
+ TypeMirror returnType = ee.getReturnType();
+ Element returnTypeElement = processingEnv.getTypeUtils().asElement(
+ returnType);
+ return returnTypeElement == null;
+ }
+
+ private boolean hasResultType(ExecutableElement ee, Element typeElement) {
+ TypeMirror returnType = ee.getReturnType();
+ Element returnTypeElement = processingEnv.getTypeUtils().asElement(
+ returnType);
+ return returnTypeElement.equals(typeElement);
+ }
+
+ private TypeElement getTypeElement(Class> cls) {
+ TypeElement typeElement = processingEnv.getElementUtils()
+ .getTypeElement(cls.getCanonicalName());
+ return typeElement;
+ }
+
+ private IllegalArgumentException error(Element e, String msg) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(e.getEnclosingElement().toString());
+ sb.append("."); //NOI18N
+ sb.append(e.getSimpleName());
+ sb.append(":"); //NOI18N
+ sb.append(System.lineSeparator());
+ sb.append(msg);
+ return new IllegalArgumentException(sb.toString());
+ }
+}
--- a/api.intent/src/org/netbeans/modules/intent/SettableResult.java
+++ a/api.intent/src/org/netbeans/modules/intent/SettableResult.java
@@ -0,0 +1,91 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2014 Sun Microsystems, Inc.
+ */
+package org.netbeans.modules.intent;
+
+import java.util.concurrent.CountDownLatch;
+import org.netbeans.spi.intent.Result;
+import org.openide.util.Parameters;
+
+/**
+ *
+ * @author jhavlin
+ */
+public final class SettableResult implements Result {
+
+ private final CountDownLatch latch = new CountDownLatch(1);
+
+ private Object result = null;
+ private Exception exception = null;
+
+ public Object getResult() {
+ try {
+ latch.await();
+ } catch (InterruptedException ex) {
+ setException(ex);
+ latch.countDown();
+ }
+ return result;
+ }
+
+ public synchronized Exception getException() {
+ try {
+ latch.await();
+ } catch (InterruptedException ex) {
+ setException(ex);
+ latch.countDown();
+ }
+ return exception;
+ }
+
+ @Override
+ public synchronized void setResult(Object result) {
+ this.result = result;
+ latch.countDown();
+ }
+
+ @Override
+ public synchronized void setException(Exception exception) {
+ Parameters.notNull("exception", exception); //NOI18N
+ this.exception = exception;
+ latch.countDown();
+ }
+}
--- a/api.intent/src/org/netbeans/spi/intent/IntentHandlerRegistration.java
+++ a/api.intent/src/org/netbeans/spi/intent/IntentHandlerRegistration.java
@@ -0,0 +1,90 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2014 Sun Microsystems, Inc.
+ */
+package org.netbeans.spi.intent;
+
+import java.util.regex.Pattern;
+
+/**
+ * Annotation for registering Intent handlers into the application.
+ * See {@link org.netbeans.spi.intent} for more info and examples.
+ *
+ * @see org.netbeans.spi.intent
+ */
+public @interface IntentHandlerRegistration {
+
+ /**
+ * Position of the handler. The lesser value, the bigger priority.
+ *
+ * @return The position.
+ */
+ int position();
+
+ /**
+ * List of supported action types. To support all actions, use "*" wildcard.
+ *
+ * @return List of supported action types. If some of contained values
+ * equals "*", all actions are supported.
+ */
+ String[] actions();
+
+ /**
+ * Pattern of supported URIs. See {@link Pattern}. Examples: To handle
+ * all URIs, use ".*", to handle http adresses, use "http://.*".
+ *
+ * @return The URI pattern.
+ */
+ String uriPattern();
+
+ /**
+ * Display name of this handler. Bundle keys can be used here.
+ *
+ * @return The display name.
+ */
+ String displayName();
+
+ /**
+ * Identifier for an icon, e.g. path or URI.
+ *
+ * @return Icon identifier.
+ */
+ String icon() default "";
+}
--- a/api.intent/src/org/netbeans/spi/intent/Result.java
+++ a/api.intent/src/org/netbeans/spi/intent/Result.java
@@ -0,0 +1,67 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2014 Sun Microsystems, Inc.
+ */
+package org.netbeans.spi.intent;
+
+import org.netbeans.api.annotations.common.NullAllowed;
+
+/**
+ * Object passed to intent handler which should be notified about computed
+ * result.
+ *
+ * @author jhavlin
+ */
+public interface Result {
+
+ /**
+ * Set computed result.
+ *
+ * @param result The result.
+ */
+ public void setResult(@NullAllowed Object result);
+
+ /**
+ * Set encountered exception.
+ *
+ * @param exception The exception.
+ */
+ public void setException(Exception exception);
+}
--- a/api.intent/src/org/netbeans/spi/intent/package-info.java
+++ a/api.intent/src/org/netbeans/spi/intent/package-info.java
@@ -0,0 +1,105 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2014 Sun Microsystems, Inc.
+ */
+/**
+ * SPI for Intent handlers.
+ *
+ * Handling some type of Intents is as simple as registering a method using
+ * annotation {@link org.netbeans.spi.intent.IntentHandlerRegistration}.
+ *
+ *
+ * Currently two types of handling methods are supported:
+ *
+ *
+ *
+ * Public static method taking {@link org.netbeans.api.intent.Intent} and
+ * returning {@link java.lang.Object}. This method will be invoked in a
+ * background thread. It is suitable if no waiting for asynchronous operations
+ * is needed and when the method will finish reasonably quickly (so that it
+ * will not block execution of other intents).
+ *
+ *
+ * Public static method taking {@link org.netbeans.api.intent.Intent} and
+ * {@link org.netbeans.spi.intent.Result} with no return type (void). It will be
+ * invoked in a background thread, but it can simply pass the result object to
+ * other threads. When the computation is finished, either
+ * {@link org.netbeans.spi.intent.Result#setException(java.lang.Exception)} or
+ * {@link org.netbeans.spi.intent.Result#setResult(java.lang.Object)}
+ * MUST be called on the result object.
+ *
+ *
+ * See examples:
+ * Basic handler:
+ *
+ * @{@link org.netbeans.spi.intent.IntentHandlerRegistration}(
+ * displayName = "Show my item in MyEditor",
+ * position = 800,
+ * uriPattern = "myscheme://.*",
+ * actions = {Intent.ACTION_VIEW, Intent.ACTION_EDIT}
+ * )
+ * public static Object handleIntent({@link org.netbeans.api.intent.Intent} intent) {
+ * SomeType result = parseAndPerformIntentSomehow(intent);
+ * return result;
+ * }
+ *
+ * Handler that uses {@link org.netbeans.spi.intent.Result}:
+ *
+ * @{@link org.netbeans.spi.intent.IntentHandlerRegistration}(
+ * displayName = "Show my item in MyEditor",
+ * position = 800,
+ * uriPattern = "myscheme://.*",
+ * actions = "*"
+ * )
+ * public static void handleIntent(final {@link org.netbeans.api.intent.Intent} intent, final {@link org.netbeans.spi.intent.Result} result) {
+ * EventQueue.invokeLater(new Runnable() {
+ * public void run() {
+ * try {
+ * Object value = doSomethingInEDT(intent);
+ * result.setResult(value);
+ * } catch (Exception e) {
+ * result.setException(e);
+ * }
+ * }
+ * });
+ * }
+ *
+ */
+package org.netbeans.spi.intent;
--- a/api.intent/test/unit/src/org/netbeans/api/intent/IntentTest.java
+++ a/api.intent/test/unit/src/org/netbeans/api/intent/IntentTest.java
@@ -0,0 +1,326 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2014 Sun Microsystems, Inc.
+ */
+package org.netbeans.api.intent;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.Semaphore;
+import org.junit.Test;
+import static org.junit.Assert.*;
+import org.junit.Before;
+import org.netbeans.spi.intent.IntentHandlerRegistration;
+import org.netbeans.spi.intent.Result;
+import org.openide.util.Exceptions;
+
+/**
+ *
+ * @author jhavlin
+ */
+public class IntentTest {
+
+ private static boolean handled = false;
+
+ private static final String HANDLED_BY_TEST = "Greetings from Test";
+ private static final String HANDLED_BY_NB = "Greetings from NB";
+ private static final String HANDLED_BY_NB_PARAM
+ = "Greetings from NB parametrized";
+ private static final String HANDLED_BY_BROKEN = "";
+ private static final String HANDLED_BY_BROKEN_SB = "";
+ private static final String HANDLED_BY_SETBACK = "Greetings from setback";
+
+ @Before
+ public void setUp() {
+ handled = false;
+ }
+
+ @Test
+ public void testOpenUri() throws URISyntaxException, InterruptedException,
+ ExecutionException {
+
+ Future res3 = new Intent("TEST",
+ new URI("scheme://x/y/z/")).execute();
+ assertNotNull(res3.get());
+
+ assertFalse(handled);
+ Future res2 = new Intent(Intent.ACTION_VIEW,
+ new URI("test://a/b/c/")).execute();
+ Exception e = null;
+ try {
+ assertNotNull(res2.get());
+ } catch (InterruptedException | ExecutionException ex) {
+ e = ex;
+ }
+ assertNotNull(e);
+ }
+
+ @Test
+ @SuppressWarnings("ThrowableResultIgnored")
+ public void testSelectAppropriateHandler()
+ throws URISyntaxException, InterruptedException,
+ ExecutionException {
+
+ Future x0 = new Intent("NONE",
+ new URI("unsupported://resource")).execute();
+
+ Exception e = null;
+ try {
+ x0.get();
+ } catch (InterruptedException | ExecutionException ex) {
+ e = ex;
+ }
+ assertTrue(e instanceof ExecutionException
+ && (e.getCause() instanceof NoAvailableHandlerException));
+
+ Future x1 = new Intent("NONE",
+ new URI("broken://resource")).execute();
+ try {
+ x1.get();
+ } catch (InterruptedException | ExecutionException ex) {
+ e = ex;
+ }
+ assertTrue(e instanceof ExecutionException);
+
+ Future x2 = new Intent("NONE",
+ new URI("brokensb://resource")).execute();
+ try {
+ x2.get();
+ } catch (InterruptedException | ExecutionException ex) {
+ e = ex;
+ }
+ assertTrue(e instanceof ExecutionException);
+
+ Future f0 = new Intent("TEST",
+ new URI("unsupported://resource")).execute();
+ assertEquals(HANDLED_BY_TEST, f0.get());
+
+ Future f1 = new Intent(Intent.ACTION_VIEW,
+ new URI("netbeans://resource")).execute();
+ assertEquals(HANDLED_BY_NB, f1.get());
+
+ Future f2 = new Intent(Intent.ACTION_VIEW,
+ new URI("netbeans://resource?someParam=x")).execute();
+ assertEquals(HANDLED_BY_NB, f2.get());
+
+ Future f3 = new Intent(Intent.ACTION_VIEW,
+ new URI("netbeans://resource?x=y&requiredParam=123")).execute();
+ assertEquals(HANDLED_BY_NB_PARAM, f3.get());
+
+ Future f4 = new Intent(Intent.ACTION_VIEW,
+ new URI("setback://resource")).execute();
+ assertEquals(HANDLED_BY_SETBACK, f4.get());
+ }
+
+ @Test
+ public void testExecutionWithCallback() throws URISyntaxException, InterruptedException, ExecutionException {
+
+ class CheckingCallback implements Callback {
+
+ private final Semaphore s = new Semaphore(0);
+
+ private Exception lastException = null;
+ private Object lastResult = null;
+
+ @Override
+ public void success(Object result) {
+ lastException = null;
+ lastResult = result;
+ s.release();
+ }
+
+ @Override
+ public void failure(Exception exception) {
+ lastException = exception;
+ lastResult = null;
+ s.release();
+ }
+
+ public void checkLastResult(Object expectedResult) {
+ try {
+ s.acquire();
+ } catch (InterruptedException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ assertEquals(expectedResult, lastResult);
+ }
+
+ public void checkLastFailure(Class extends Exception> ec) {
+ try {
+ s.acquire();
+ } catch (InterruptedException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ assertNotNull(lastException);
+ assertEquals(ec, lastException.getClass());
+ }
+ }
+
+ CheckingCallback cb = new CheckingCallback();
+
+ new Intent("NONE", new URI("broken://resource")).execute(cb);
+ cb.checkLastFailure(RuntimeException.class);
+
+ new Intent("NONE", new URI("brokensb://resource")).execute(cb);
+ cb.checkLastFailure(RuntimeException.class);
+
+ new Intent("NONE", new URI("unsupported://resource")).execute(cb);
+ cb.checkLastFailure(NoAvailableHandlerException.class);
+
+ new Intent("TEST",
+ new URI("unsupported://resource")).execute(cb);
+ cb.checkLastResult(HANDLED_BY_TEST);
+
+ new Intent(Intent.ACTION_VIEW,
+ new URI("netbeans://resource")).execute(cb);
+ cb.checkLastResult(HANDLED_BY_NB);
+
+ new Intent(Intent.ACTION_VIEW,
+ new URI("netbeans://resource?someParam=x")).execute(cb);
+ cb.checkLastResult(HANDLED_BY_NB);
+
+ new Intent(Intent.ACTION_VIEW,
+ new URI("netbeans://resource?x=y&requiredParam=123")).execute(cb);
+ cb.checkLastResult(HANDLED_BY_NB_PARAM);
+
+ new Intent(Intent.ACTION_VIEW,
+ new URI("setback://resource")).execute(cb);
+ cb.checkLastResult(HANDLED_BY_SETBACK);
+ }
+
+ /**
+ * Handler that claims to support all URI patterns, but that actually
+ * accepts only scheme "test".
+ *
+ * @param intent
+ * @return
+ */
+ @SuppressWarnings("PublicInnerClass")
+ @IntentHandlerRegistration(
+ displayName = "Test",
+ position = 999,
+ uriPattern = ".*",
+ actions = "TEST")
+ public static Object handleIntent(Intent intent) {
+ return HANDLED_BY_TEST;
+ }
+
+ /**
+ * Handler for URIs with scheme "netbeans".
+ *
+ * @param intent
+ * @return
+ */
+ @SuppressWarnings("PublicInnerClass")
+ @IntentHandlerRegistration(
+ displayName = HANDLED_BY_NB,
+ position = 998,
+ uriPattern = "netbeans://.*",
+ actions = "*")
+ public static Object handleNetBeansIntent(Intent intent) {
+ return HANDLED_BY_NB;
+ }
+
+ /**
+ * Handler for URIs with scheme "netbeans" and parameter "requiredParam".
+ *
+ * @param intent
+ * @return
+ */
+ @SuppressWarnings("PublicInnerClass")
+ @IntentHandlerRegistration(
+ displayName = HANDLED_BY_NB_PARAM,
+ position = 997,
+ uriPattern = "netbeans://.*[?&]requiredParam=.+",
+ actions = {Intent.ACTION_VIEW, Intent.ACTION_EDIT})
+ public static Object handleParametrizedNetBeansIntent(Intent intent) {
+ return HANDLED_BY_NB_PARAM;
+ }
+
+ /**
+ * Handler for URIs with scheme "broken".
+ *
+ * @param intent
+ * @return
+ */
+ @SuppressWarnings("PublicInnerClass")
+ @IntentHandlerRegistration(
+ displayName = HANDLED_BY_BROKEN,
+ position = 997,
+ uriPattern = "broken://.*",
+ actions = "*")
+ public static Object handleBroken(Intent intent) {
+ throw new RuntimeException("Intentionally broken");
+ }
+
+ /**
+ * Handler for URIs with scheme "brokensb".
+ *
+ * @param intent
+ * @param result
+ */
+ @SuppressWarnings("PublicInnerClass")
+ @IntentHandlerRegistration(
+ displayName = HANDLED_BY_BROKEN_SB,
+ position = 997,
+ uriPattern = "brokensb://.*",
+ actions = "*")
+ public static void handleBrokenSb(Intent intent, Result result) {
+ result.setException(new RuntimeException("Intentionally broken"));
+ }
+
+ /**
+ * Handler for URIs with scheme "setback".
+ *
+ * @param intent
+ * @param result
+ */
+ @SuppressWarnings("PublicInnerClass")
+ @IntentHandlerRegistration(
+ displayName = HANDLED_BY_SETBACK,
+ position = 997,
+ uriPattern = "setback://.*",
+ actions = "*")
+ public static void handleSetBack(Intent intent, Result result) {
+ result.setResult(HANDLED_BY_SETBACK);
+ }
+}
--- a/nbbuild/build.properties
+++ a/nbbuild/build.properties
@@ -100,6 +100,7 @@
config.javadoc.stable=\
api.annotations.common,\
api.html4j,\
+ api.intent,\
api.io,\
api.maven,\
api.templates,\
--- a/nbbuild/cluster.properties
+++ a/nbbuild/cluster.properties
@@ -195,6 +195,7 @@
nb.cluster.platform=\
api.annotations.common,\
api.html4j,\
+ api.intent,\
api.io,\
api.progress,\
api.progress.compat8,\
--- a/nbbuild/javadoctools/links.xml
+++ a/nbbuild/javadoctools/links.xml
@@ -240,5 +240,6 @@
+
--- a/nbbuild/javadoctools/properties.xml
+++ a/nbbuild/javadoctools/properties.xml
@@ -239,5 +239,6 @@
+
--- a/nbbuild/javadoctools/replaces.xml
+++ a/nbbuild/javadoctools/replaces.xml
@@ -239,5 +239,6 @@
+