blob: 99e5264f34cae69d5ad9a9ef888dd9845da28434 [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.
*/
package org.netbeans.modules.apisupport.hints;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.ModifiersTree;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import org.netbeans.api.java.source.GeneratorUtilities;
import org.netbeans.api.java.source.TreeMaker;
import org.netbeans.api.java.source.WorkingCopy;
import org.netbeans.spi.editor.hints.Severity;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.NotifyDescriptor.Message;
import org.openide.filesystems.FileObject;
import org.openide.util.NbBundle.Messages;
import org.openide.util.NbCollections;
import org.openide.util.lookup.ServiceProvider;
import static org.netbeans.modules.apisupport.hints.Bundle.*;
/**
* #191236: {@code ActionRegistration} conversion.
*/
@ServiceProvider(service=Hinter.class)
public class ActionRegistrationHinter implements Hinter {
public @Override void process(final Context ctx) throws Exception {
final FileObject file = ctx.file();
final Object instanceCreate = ctx.instanceAttribute(file);
if (instanceCreate == null) {
return;
}
if ("method:org.openide.awt.Actions.alwaysEnabled".equals(instanceCreate)) {
ctx.addStandardAnnotationHint(new Callable<Void>() {
public @Override Void call() throws Exception {
if (!annotationsAvailable(ctx)) {
return null;
}
ctx.findAndModifyDeclaration(file.getAttribute("literal:delegate"), new RegisterAction(ctx, false));
return null;
}
});
} else if ("method:org.openide.awt.Actions.checkbox".equals(instanceCreate)) {
// #193279: no associated annotation available
} else if ("method:org.openide.awt.Actions.callback".equals(instanceCreate) || "method:org.openide.awt.Actions.context".equals(instanceCreate)) {
ctx.addHint(Severity.WARNING, ctx.standardAnnotationDescription()/* XXX no fixes yet */);
} else if ("method:org.openide.windows.TopComponent.openAction".equals(instanceCreate)) {
// XXX pending #191407: @OpenActionRegistration (w/ @ActionID and @ActionReference)
// (could also do @Registration but would be a separate Hinter)
// (@Description probably needed but harder since need to remove method overrides)
} else if (file.getPath().startsWith("Actions/")) {
// Old-style eager action of some variety.
ctx.addStandardAnnotationHint(new Callable<Void>() {
public @Override Void call() throws Exception {
if (!annotationsAvailable(ctx)) {
return null;
}
ctx.findAndModifyDeclaration(instanceCreate, new RegisterAction(ctx, true));
return null;
}
});
}
}
@Messages("ActionRegistrationHinter.missing_org.openide.awt=You must add a dependency on org.openide.awt (7.27+) before using this fix.")
private boolean annotationsAvailable(Context ctx) {
if (ctx.canAccess("org.openide.awt.ActionReferences")) {
return true;
} else {
DialogDisplayer.getDefault().notify(new Message(ActionRegistrationHinter_missing_org_openide_awt(), NotifyDescriptor.WARNING_MESSAGE));
return false;
}
}
private static class RegisterAction implements Context.ModifyDeclarationTask {
private final Context ctx;
private final boolean eager;
RegisterAction(Context ctx, boolean eager) {
this.ctx = ctx;
this.eager = eager;
}
public @Override void run(WorkingCopy wc, Element declaration, ModifiersTree modifiers) throws Exception {
Map<String,Object> params = new HashMap<String,Object>();
FileObject file = ctx.file();
params.put("category", file.getParent().getPath().substring("Actions/".length()));
params.put("id", file.getName().replace('-', '.'));
ModifiersTree nue = ctx.addAnnotation(wc, modifiers, "org.openide.awt.ActionID", null, params);
params.clear();
String displayName = ctx.bundlevalue(file.getAttribute("literal:displayName"), declaration);
if (displayName == null) {
// @ActionRegistration requires this attr, even though it is unused for eager actions.
displayName = "#TODO";
}
params.put("displayName", displayName);
params.put("iconBase", file.getAttribute("iconBase"));
Object noIconInMenu = file.getAttribute("noIconInMenu");
if (noIconInMenu instanceof Boolean) {
params.put("iconInMenu", !((Boolean) noIconInMenu));
}
params.put("asynchronous", file.getAttribute("asynchronous"));
if (eager) {
params.put("lazy", false);
} else {
// XXX specify lazy=true if implements one of the 5 specials even though using Actions.* factory (but probably rare)
}
nue = ctx.addAnnotation(wc, nue, "org.openide.awt.ActionRegistration", null, params);
ctx.delete(file);
TreeMaker make = wc.getTreeMaker();
List<AnnotationTree> anns = new ArrayList<AnnotationTree>();
for (FileObject shadow : NbCollections.iterable(file.getFileSystem().getRoot().getData(true))) {
if (!shadow.hasExt("shadow")) {
continue;
}
if (file.getPath().equals(shadow.getAttribute("originalFile"))) {
List<ExpressionTree> arguments = new ArrayList<ExpressionTree>();
arguments.add(make.Assignment(make.Identifier("path"), make.Literal(shadow.getParent().getPath())));
String name = shadow.getName();
if (!name.equals(file.getName())) {
arguments.add(make.Assignment(make.Identifier("name"), make.Literal(name)));
}
Object pos = shadow.getAttribute("position");
if (pos instanceof Integer) {
arguments.add(make.Assignment(make.Identifier("position"), make.Literal(pos)));
}
// XXX maybe look for nearby separators?
TypeElement ann = wc.getElements().getTypeElement("org.openide.awt.ActionReference");
if (ann == null) {
throw new IllegalArgumentException("Could not find ActionReference in classpath");
}
anns.add(make.Annotation(make.QualIdent(ann), arguments));
ctx.delete(shadow);
}
}
if (anns.size() == 1) {
nue = make.addModifiersAnnotation(nue, anns.get(0));
} else if (!anns.isEmpty()) {
TypeElement ann = wc.getElements().getTypeElement("org.openide.awt.ActionReferences");
if (ann == null) {
throw new IllegalArgumentException("Could not find ActionReferences in classpath");
}
nue = make.addModifiersAnnotation(nue, make.Annotation(make.QualIdent(ann), Collections.singletonList(make.Assignment(make.Identifier("value"), make.NewArray(null, Collections.<ExpressionTree>emptyList(), anns)))));
}
wc.rewrite(modifiers, GeneratorUtilities.get(wc).importFQNs(nue));
}
}
}