blob: 15ca27d16f90de3a20467298128de8eeaa6f0764 [file] [log] [blame]
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
= DevFaqLookup
:jbake-type: wiki
:jbake-tags: wiki, devfaq, needsreview
:jbake-status: published
:keywords: Apache NetBeans wiki DevFaqLookup
:description: Apache NetBeans wiki DevFaqLookup
:toc: left
:toc-title:
:syntax: true
=== What is a Lookup?
link:http://bits.netbeans.org/dev/javadoc/org-openide-util-lookup/org/openide/util/Lookup.html[Lookup] is a mechanism for finding instances of objects. It is pervasively used in NetBeans APIs. The general pattern is that you pass a Class object and get back an instance of that class or null. See the Javadoc for links to articles describing its inspiration and purpose.
The simplest way to think of `Lookup` is that it is a `Map` where the keys are `Class` objects and the value for each key is an instance of the key class.
There is the link:DevFaqLookupDefault.asciidoc[global lookup] which is used to find objects (often, but not always, singletons) that are registered throughout the system. Also, many types of objects have a method getLookup() that enables other code to get things specific to that object. In particular, `link:DevFaqWhatIsANode.asciidoc[Node]`s and `Project` objects have a `Lookup`.
The primary purpose of Lookup is decoupling - it makes it possible to use generic objects to get very specific information, without having to cast objects to a specific type. Confused yet? It's simple. Take the example of link:http://bits.netbeans.org/dev/javadoc/org-openide-awt/org/netbeans/api/actions/Openable.html[Openable] - it has one method, `open()` that will open a file in the editor.
Say that I want to write some code that will open the selected file when the user does something. It could be an Action, a button, or maybe my code has just created a file and I want to open it. This is what I will do:
[source,java]
----
Lookup selectedContext = Utilities.actionsGlobalContext();
Openable o = selectedContext.lookup(Openable.class);
if (o != null) {
o.open();
}
----
The power of all this is in the level of decoupling it provides: My code that wants to open the file does not have to know anything at all about what happens when the file is opened, or what kind of file it is, or what module supports opening it. And the module that supports opening it does not need to know anything about who is going to open it. They both simply share a dependency on the abstract interface `Openable`. So either one can be replaced without affecting the other at all.
This brings the link:apidesign:MVC.asciidoc[MVC] design pattern into link:apidesign:DCI.asciidoc[modular loosely coupled] world.
A good example of this is in the link:http://platform.netbeans.org/tutorials/nbm-povray-1.html[POV-Ray tutorial]. It launches an external process that generates a `.png` file. When the process ends, it wants to open it, so it does the following:
[source,java]
----
FileObject fob = FileUtil.toFileObject(new File(pathWePassedToProcess));
if (fob != null) { //the process succeeded
DataObject dob = DataObject.find(fob);
Openable oc = dob.getLookup().lookup(Openable.class);
if (oc != null) { //the Image module is installed
oc.open();
}
}
----
The fact is that it is the Image module that makes it possible to open `.png` files in link:NetBeans.asciidoc[NetBeans]. But the POV-Ray tutorial does not need to know or care that the Image module exists, or what it does - it simply says "open this".
The common pattern you'll see for Lookup usage is one where there are three components:
* Module A is pure link:API.asciidoc[API] - it provides some interfaces
* Module B depends on A and implements those interfaces, providing them from a Node or Project or such
* Module C wants to display some UI or do something with objects that implement those interfaces. It also depends on A, but does not need to know about B at all; either can be replaced independently, and the other will still function.
For global services, the model is more simple - typically there will be some singleton object, implemented as an abstract class:
[source,java]
----
public abstract class GlobalService {
public abstract void doSomething(Something arg);
public static GlobalService getDefault() {
GlobalService result = Lookup.getDefault().lookup(GlobalService.class);
if (result == null) {
result = new NoOpGlobalService();
}
return result;
}
private static class NoOpGlobalService extends GlobalService {
public void doSomething(Something arg) {}
}
}
----
Some other module entirely actually registers an implementation of this interface in the link:DevFaqLookupDefault.asciidoc[default Lookup]. link:http://bits.netbeans.org/dev/javadoc/org-openide-awt/org/openide/awt/StatusDisplayer.html[StatusDisplayer] is a good example of this pattern.
==== What if multiple objects of the same type should be available?
A `Lookup` is not limited to containing one singleton of any type. If there may be more than one of a given type in a Lookup, the syntax is slightly different:
[source,java]
----
Collection<? extends SomeIface> c = Lookup.getDefault().lookupAll(SomeIface.class);
----
*Note:* In NetBeans versions prior to 6.0 you need to use `Lookup.Template` and `Lookup.Result.allInstances()`, because the `lookupAll()` method was not created until 6.0.
The `Lookup.Result` can be listened on for changes in the result of the query. It is often useful to think of a Lookup as a _space_ in which objects appear and disappear, and your code can respond as that happens (the following code uses the NB 6.0 `lookupResult` method - just use the pattern above with the `Lookup.Template` for NetBeans 5):
[source,java]
----
class ObjectInterestedInFooObjects implements LookupListener {
final Lookup.Result<Foo> result; //result object is weakly referenced inside Lookup
ObjectInterestedInFooObjects() {
result = someLookup.lookupResult(Foo.class);
result.addLookupListener(this);
resultChanged(null);
}
public void resultChanged(LookupEvent evt) {
Collection<? extends Foo> c = result.allInstances();
// do something with the result
}
}
----
Another question is, on the side that's providing the lookup, if you _have_ a collection already, how can you expose that in a `Lookup`. For that, you can create your own `AbstractLookup` and use `InstanceContent` to provide the collection of objects that belong in your `Lookup`.
If you need to merge together more than one lookup (for example, the lookup provided from `Node.getCookieSet().getLookup()` and one of your own which you will add and remove objects from), you can simply use [link:http://bits.netbeans.org/dev/javadoc/org-openide-util-lookup/org/openide/util/lookup/ProxyLookup.html[http://bits.netbeans.org/dev/javadoc/org-openide-util-lookup/org/openide/util/lookup/ProxyLookup.html] `ProxyLookup`] - i.e. `new ProxyLookup (lookup1, lookup2, Lookups.singleton(someObject), ...)`
Objects in a `Lookup` often are not instantiated until the first time they are requested; depending on the implementation, they may be weakly referenced, so that if an object is not used for a while, it can be garbage collected to save memory. So `Lookup` additionally enables lazy instantiation of objects, which is useful for performance reasons.
=== Apache Migration Information
The content in this page was kindly donated by Oracle Corp. to the
Apache Software Foundation.
This page was exported from link:http://wiki.netbeans.org/DevFaqLookup[http://wiki.netbeans.org/DevFaqLookup] ,
that was last modified by NetBeans user Skygo
on 2013-12-14T17:13:46Z.
*NOTE:* This document was automatically converted to the AsciiDoc format on 2018-02-07, and needs to be reviewed.