The basic question I asked paulweb515 is as follows:
You're writing an extension point. You want an id and a label, as well as
lots of other stuff / methods. So you make an interface for it that others
must implement.
public interface IPackageType {
public String getId();
public String getLabel();
public IPackage createDefaultConfiguration(IProject project,
IProgressMonitor monitor);
public int getSupportFor (IProject project);
}
In your extension point, you require a class, but you also want to require
the id and label. What is the best way to do it?
paulweb515 had 3 suggestions:
<paulweb515> rawblem: I've seen 3 common usages
<paulweb515> rawblem: 1) Tell you users to implement IPackageType that has
setId(*) as well as getId(*) ... then you can set your user's data when you
create the executable extension
If you just let the extender implement IChef, he'll be filling in id and
label twice, once in the extension point, and once in API.
So paulweb suggests setters, but that's not a great idea because then anyone
who gets a reference to your object can
change the ID and label. Obviously that's a glaring error.
<paulweb515> rawblem: 2) Like ViewPart, create an abstract base class that
they must use instead of just an interface. ViewPart uses
setInitializationData(*) to store the config element for the client to use
in a protected method, and extracts id, label etc into the base abstract
class ... but they're private, and it only provides public final String
getId() so they can't change it
This sounds like the best way, I'll look at it momentarily.
<paulweb515> or 3) don't store the ID in the object, have that ID be part of
an association that your extension point knows.
Not exactly the best way, because then given an IChef, you cannot get his id
or label.
Things I've done in the past:
Let the user extend AbstractPackageDelegate, then when loading the
extension points, do as follows:
public class AbstractPackageDelegate {
public abstract IPackage createDefaultConfiguration(IProject project,
IProgressMonitor monitor);
public abstract int getSupportFor (IProject project);
}
public class PackageTypeWrapper implements IPackageType {
private AbstractPackageDelegate delegate;
private IConfigurationElement element;
public PackageTypeWrapper(IConfigurationElement element) {
this.element = element;
delegate = element.createExecutableExtension("class");
}
public String getId() { return element.getAttribute("id"); }
public String getLabel() { return element.getAttribute("label"); }
public IPackage createDefaultConfiguration(IProject project,
IProgressMonitor monitor) {
return delegate.createDefaultConfiguration(project,
monitor);
}
public int getSupportFor (IProject project) {
return delegate.getSupportFor(project);
}
}
The problem with this example, in this specific IPackageType example, is
that in JarPackageType, marshall uses the following code:
jar.setPackageType(this);
In the above example, "this", isnt an IPackageType. It's an incomplete
piece
of an IPackageType that will need to be wrapped.
A possible fix is:
public IPackage createDefaultConfiguration(IProject project,
IProgressMonitor monitor) {
IPackage obj = delegate.createDefaultConfiguration(project,
monitor);
obj.setPackageType(this);
return obj;
}
... but clearly that's not ideal or intuitive.
//***** Paulweb's option 2 *********//
public abstract class AbstractPackageType implements IPackageType {
private String id;
private String label;
private boolean initailized = false;
public void setInitializationData(IConfigurationElement el) {
if( !initialized) {
id = el.getAttribute("id");
label = el.getAttribute("label");
}
}
public String getId() { return id; }
public String getLabel() { return label; }
public abstract int getSupportFor (IProject project);
public abstract IPackage createDefaultConfiguration(IProject project,
IProgressMonitor monitor);
}
// creation
AbstractPackageType extender =
(AbstractPackageType)configElement.createExecutableExtension("class");
extender.setInitializationData(configElement);
In this way, all users who get a reference to the IPackageType can get the
id and label, but no users can change it arbitrarily.
Someone could still theoretically write their extension of
AbstractPackageType to override the setInitializationData and make it
easy to change, but at least then it's the extender willfully allowing it to
happen, instead of our abstract class having a public setter for all to use.
All are welcome to give forth their suggestions or other best practices they
use WRT extension points ;)
I'll probably be using a methodology similar to the one paulweb515
suggested.
- Rob