blob: abeb55548f36496b6f8ee8103a6415c0b8b27694 [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.project.ui.wizard.common;
import org.netbeans.modules.apisupport.project.api.BasicWizardPanel;
import org.netbeans.modules.apisupport.project.api.BasicVisualPanel;
import java.awt.Component;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.NoSuchElementException;
import java.util.Set;
import javax.swing.event.ChangeListener;
import javax.swing.text.BadLocationException;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.modules.editor.indent.api.Reformat;
import org.netbeans.api.java.project.JavaProjectConstants;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectUtils;
import org.netbeans.api.project.SourceGroup;
import org.netbeans.api.project.Sources;
import org.netbeans.editor.BaseDocument;
import org.netbeans.modules.apisupport.project.api.Util;
import org.netbeans.modules.apisupport.project.spi.NbModuleProvider;
import org.netbeans.spi.project.ui.templates.support.Templates;
import org.openide.ErrorManager;
import org.openide.WizardDescriptor;
import org.openide.cookies.EditorCookie;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataObject;
import org.openide.loaders.DataObjectNotFoundException;
import org.openide.util.HelpCtx;
import static org.netbeans.modules.apisupport.project.ui.wizard.common.Bundle.*;
import org.openide.util.NbBundle.Messages;
/**
* Convenient class for implementing {@link org.openide.WizardDescriptor.InstantiatingIterator}.
*
* @author Radek Matous
*/
public abstract class BasicWizardIterator implements WizardDescriptor.AsynchronousInstantiatingIterator<WizardDescriptor> {
private int position = 0;
private BasicWizardIterator.PrivateWizardPanel[] wizardPanels;
/** Create a new wizard iterator. */
protected BasicWizardIterator() {}
/** @return all panels provided by this {@link org.openide.WizardDescriptor.InstantiatingIterator} */
protected abstract BasicWizardIterator.Panel[] createPanels(WizardDescriptor wiz);
/** Basic visual panel.*/
public abstract static class Panel extends BasicVisualPanel {
protected Panel(WizardDescriptor wiz) {
super(wiz);
}
/**
* Returned name is used by a wizard. e.g. on its left side in the
* <em>step list</em>.
* @return name of panel
*/
protected abstract String getPanelName();
/**
* Gives a chance to store an instance of {@link
* BasicWizardIterator.BasicDataModel}. It is called when a panel is
* going to be <em>hidden</em> (e.g. when switching to next/previous
* panel).
*/
protected abstract void storeToDataModel();
/**
* Gives a chance to refresh a panel (usually by reading a state of an
* instance of {@link BasicWizardIterator.BasicDataModel}. It is called
* when a panel is going to be <em>displayed</em> (e.g. when switching
* from next/previous panel).
*/
protected abstract void readFromDataModel();
protected abstract HelpCtx getHelp();
}
/** DataModel that is passed through individual panels.*/
public static class BasicDataModel {
private Project project;
private NbModuleProvider module;
private SourceGroup sourceRootGroup;
private String packageName;
/** Creates a new instance of NewFileDescriptorData */
public BasicDataModel(WizardDescriptor wiz) {
Project tmpProject = Templates.getProject(wiz);
if (tmpProject == null) {
throw new IllegalArgumentException();
}
module = tmpProject.getLookup().lookup(NbModuleProvider.class);
if (module == null) {
throw new IllegalArgumentException(tmpProject.getClass().toString());
}
project = tmpProject;
// #66339 need to prefetch the packagename and populate data with it..
FileObject fo = Templates.getTargetFolder(wiz);
if (fo != null) {
Sources srcs = ProjectUtils.getSources(project); // #63247: don't use lookup directly
SourceGroup[] grps = srcs.getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
for (int i = 0; i < grps.length; i++) {
if (FileUtil.isParentOf(grps[i].getRootFolder(), fo)) {
String relPath = FileUtil.getRelativePath(grps[i].getRootFolder(), fo);
relPath = relPath.replace('/', '.');
setPackageName(relPath);
break;
}
}
}
}
public Project getProject() {
return project;
}
public NbModuleProvider getModuleInfo() {
return module;
}
public String getPackageName() {
return packageName;
}
public void setPackageName(String packageName) {
this.packageName = packageName;
}
public SourceGroup getSourceRootGroup() {
if (sourceRootGroup == null) {
FileObject tempSrcRoot = getModuleInfo().getSourceDirectory();
assert tempSrcRoot != null;
Sources sources = ProjectUtils.getSources(project);
SourceGroup[] groups = sources.getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
for (int i = 0; sourceRootGroup == null && i < groups.length; i++) {
if (groups[i].getRootFolder().equals(tempSrcRoot)) {
sourceRootGroup = groups[i];
}
}
}
return sourceRootGroup;
}
public String getDefaultPackagePath(String fileName, boolean resource) {
return getDefaultPackagePath(fileName, resource, false);
}
public String getDefaultPackagePath(String fileName, boolean resource, boolean inTests) {
if (inTests) {
String path;
if (resource) {
path = getModuleInfo().getResourceDirectoryPath(true);
} else {
path = getModuleInfo().getTestSourceDirectoryPath();
}
return path + '/'
+ getPackageName().replace('.', '/') + '/' + fileName;
}
return (resource ? getModuleInfo().getResourceDirectoryPath(inTests) : getModuleInfo().getSourceDirectoryPath()) + '/' +
getPackageName().replace('.','/') + '/' + fileName;
}
/**
* Conditionally adds an operation to the given {@link
* CreatedModifiedFiles}. Result of the operation, after given
* CreatedModifiedFiles are run, is copied (into the package) icon
* representing given <code>origIconPath</code>. If the origIconPath is
* already inside the project's source directory nothing happens.
*
* @return path of the icon relative to the project's source directory
*/
public String addCreateIconOperation(CreatedModifiedFiles cmf, @NonNull FileObject originalIcon) {
String relativeIconPath = null;
if (!FileUtil.isParentOf(Util.getResourceDirectory(getProject()), originalIcon)) {
String iconPath = getDefaultPackagePath(originalIcon.getNameExt(), true);
cmf.add(cmf.createFile(iconPath, originalIcon));
relativeIconPath = getPackageName().replace('.', '/') + '/' + originalIcon.getNameExt();
} else {
relativeIconPath = FileUtil.getRelativePath(Util.getResourceDirectory(getProject()), originalIcon);
}
return relativeIconPath;
}
}
public static class InvalidProjectPanel extends Panel {
@Messages("LBL_InvalidProjectMsg=Unable to finish selected wizard. Project is either not initialized or of wrong type.")
public InvalidProjectPanel(WizardDescriptor wiz) {
super(wiz);
setError(LBL_InvalidProjectMsg());
}
@Messages("LBL_InvalidProject=Invalid Project")
protected @Override String getPanelName() {
return LBL_InvalidProject();
}
@Override
protected HelpCtx getHelp() {
return new HelpCtx(InvalidProjectPanel.class);
}
@Override
protected void readFromDataModel() {
// nothing
}
@Override
protected void storeToDataModel() {
// nothing
}
}
public void initialize(WizardDescriptor wiz) {
// mkleint: copied from the NewJavaFileWizardIterator.. there must be something painfully wrong..
String[] beforeSteps = null;
Object prop = wiz.getProperty(WizardDescriptor.PROP_CONTENT_DATA); // NOI18N
if (prop != null && prop instanceof String[]) {
beforeSteps = (String[])prop;
}
position = 0;
// #176828: do not continue with wizard if called on non-NBM project;
// can be e.g. LazyProject or project without preferred templates
Project tmpProject = Templates.getProject(wiz);
boolean isValidPrj = (tmpProject != null && tmpProject.getLookup().lookup(NbModuleProvider.class) != null);
Panel[] panels = isValidPrj ? createPanels(wiz)
: new Panel[] { new InvalidProjectPanel(wiz) };
String[] steps = BasicWizardIterator.createSteps(beforeSteps, panels);
wizardPanels = new BasicWizardIterator.PrivateWizardPanel[panels.length];
for (int i = 0; i < panels.length; i++) {
wizardPanels[i] = new BasicWizardIterator.PrivateWizardPanel(panels[i], steps, i);
}
}
// mkleint: copied from the NewJavaFileWizardIterator.. there must be something painfully wrong..
private static String[] createSteps(String[] before, BasicWizardIterator.Panel[] panels) {
assert panels != null;
// hack to use the steps set before this panel processed
int diff = 0;
if (before == null) {
before = new String[0];
} else if (before.length > 0) {
diff = ("...".equals(before[before.length - 1])) ? 1 : 0; // NOI18N
}
String[] res = new String[ (before.length - diff) + panels.length];
for (int i = 0; i < res.length; i++) {
if (i < (before.length - diff)) {
res[i] = before[i];
} else {
res[i] = panels[i - before.length + diff].getPanelName();
}
}
return res;
}
public void uninitialize(WizardDescriptor wiz) {
wizardPanels = null;
}
public String name() {
return ((BasicWizardIterator.PrivateWizardPanel)
current()).getPanel().getPanelName();
}
public boolean hasNext() {
return position < (wizardPanels.length - 1);
}
public boolean hasPrevious() {
return position > 0;
}
public void nextPanel() {
if (!hasNext()) {
throw new NoSuchElementException();
}
position++;
}
public void previousPanel() {
if (!hasPrevious()) {
throw new NoSuchElementException();
}
position--;
}
public WizardDescriptor.Panel<WizardDescriptor> current() {
return wizardPanels[position];
}
public final void addChangeListener(ChangeListener l) {}
public final void removeChangeListener(ChangeListener l) {}
public static Set<FileObject> getCreatedFiles(final CreatedModifiedFiles cmf, final Project project) throws IOException {
Collection<DataObject> toBeShown = new HashSet<DataObject>();
Set<FileObject> set = new HashSet<FileObject>();
for (String path : cmf.getCreatedPaths()) {
FileObject fo = project.getProjectDirectory().getFileObject(path);
assert fo != null : path;
formatFile(fo);
DataObject dObj = DataObject.find(fo);
if (dObj != null && toBeShown.size() < 10 && toBeShown.add(dObj)) {
set.add(fo);
}
}
return set;
}
private static BaseDocument getDocument(final FileObject fo) throws DataObjectNotFoundException, IOException {
BaseDocument doc = null;
DataObject dObj = DataObject.find(fo);
if (dObj != null) {
EditorCookie editor = dObj.getLookup().lookup(EditorCookie.class);
if (editor != null) {
doc = (BaseDocument) editor.openDocument();
}
}
return doc;
}
// copy-pasted-adjusted from org.netbeans.editor.ActionFactory.FormatAction
private static void formatFile(final FileObject fo) {
assert fo != null;
final BaseDocument doc;
try {
doc = BasicWizardIterator.getDocument(fo);
if (doc == null) {
return;
}
final Reformat reformat = Reformat.get(doc);
reformat.lock();
try {
doc.runAtomic(new Runnable() {
public void run() {
try {
reformat.reformat(0, doc.getLength());
} catch (BadLocationException x) {
throw new RuntimeException(x);
}
}
});
} finally {
reformat.unlock();
}
try {
DataObject.find(fo).getLookup().lookup(EditorCookie.class).saveDocument();
} catch (IOException e) {
Util.err.notify(ErrorManager.INFORMATIONAL, e);
}
} catch (Exception ex) {
// no disaster
ErrorManager.getDefault().log(ErrorManager.WARNING, "Cannot reformat the file: " + fo.getPath()); // NOI18N
}
}
private static final class PrivateWizardPanel extends BasicWizardPanel {
private BasicWizardIterator.Panel panel;
PrivateWizardPanel(BasicWizardIterator.Panel panel, String[] allSteps, int stepIndex) {
super(panel.getSettings());
panel.addPropertyChangeListener(this);
panel.setName(panel.getPanelName()); // NOI18N
this.panel = panel;
panel.putClientProperty(WizardDescriptor.PROP_CONTENT_SELECTED_INDEX, new Integer(stepIndex)); // NOI18N
// names of currently used steps
panel.putClientProperty(WizardDescriptor.PROP_CONTENT_DATA, allSteps); // NOI18N
}
private BasicWizardIterator.Panel getPanel() {
return panel;
}
public Component getComponent() {
return getPanel();
}
public @Override void storeSettings(WizardDescriptor wiz) {
if (WizardDescriptor.NEXT_OPTION.equals(wiz.getValue()) ||
WizardDescriptor.FINISH_OPTION.equals(wiz.getValue())) {
panel.storeToDataModel();
}
//XXX hack
wiz.putProperty("NewFileWizard_Title", null); // NOI18N
}
public @Override void readSettings(WizardDescriptor wiz) {
// mkleint - copied from someplace.. is definitely weird..
// XXX hack, TemplateWizard in final setTemplateImpl() forces new wizard's title
// this name is used in NewProjectWizard to modify the title
Object substitute = getPanel().getClientProperty("NewFileWizard_Title"); // NOI18N
if (substitute != null) {
wiz.putProperty("NewFileWizard_Title", substitute); // NOI18N
}
if (WizardDescriptor.NEXT_OPTION.equals(wiz.getValue()) || wiz.getValue() == null) {
panel.readFromDataModel();
}
}
public @Override HelpCtx getHelp() {
return getPanel().getHelp();
}
}
}