blob: 865c3320dcceb2f0bdd5c8a7f839141cf8a665db [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.javafx2.project;
import java.awt.Component;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.LinkedHashSet;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JComponent;
import javax.swing.event.ChangeListener;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.project.classpath.ProjectClassPathModifier;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectManager;
import org.netbeans.modules.java.api.common.project.ProjectProperties;
import org.netbeans.modules.javafx2.project.api.JavaFXProjectUtils;
import org.netbeans.modules.javafx2.project.fxml.ConfigureFXMLControllerPanelVisual;
import org.netbeans.spi.java.project.support.ui.SharableLibrariesUtils;
import org.netbeans.spi.project.support.ant.AntProjectHelper;
import org.netbeans.spi.project.support.ant.EditableProperties;
import org.netbeans.spi.project.ui.support.ProjectChooser;
import org.openide.DialogDisplayer;
import org.openide.ErrorManager;
import org.openide.NotifyDescriptor;
import org.openide.WizardDescriptor;
import org.openide.filesystems.FileLock;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.NbBundle;
/**
* Wizard to create a new JavaFX project
*
* @author Petr Hrebejk
* @author Anton Chechel
* @author Petr Somol
*/
public class JavaFXProjectWizardIterator implements WizardDescriptor.ProgressInstantiatingIterator {
private static final Logger LOG = Logger.getLogger(JavaFXProjectWizardIterator.class.getName());
public static enum WizardType {APPLICATION, PRELOADER, FXML, SWING, LIBRARY, EXTISTING}
static final String PROP_NAME_INDEX = "nameIndex"; // NOI18N
static final String MAIN_CLASS = "mainClass";
static final String PROP_PRELOADER_NAME = "preloaderName"; // NOI18N
static final String SHARED_LIBRARIES = "sharedLibraries"; // NOI18N
static final String FXML_NAME = "fxmlName"; // NOI18N
static final String MANIFEST_FILE = "manifest.mf"; // NOI18N
//static final String GENERATED_PRELOADER_CLASS_NAME = "SimplePreloader"; // NOI18N
// static final String GENERATED_FXML_CLASS_NAME = "Sample"; // NOI18N
private static final long serialVersionUID = 1L;
private WizardType type;
public JavaFXProjectWizardIterator() {
this(WizardType.APPLICATION);
}
public static JavaFXProjectWizardIterator fxml() {
return new JavaFXProjectWizardIterator(WizardType.FXML);
}
public static JavaFXProjectWizardIterator preloader() {
return new JavaFXProjectWizardIterator(WizardType.PRELOADER);
}
public static JavaFXProjectWizardIterator swing() {
return new JavaFXProjectWizardIterator(WizardType.SWING);
}
public static JavaFXProjectWizardIterator library() {
return new JavaFXProjectWizardIterator(WizardType.LIBRARY);
}
public static JavaFXProjectWizardIterator existing() {
return new JavaFXProjectWizardIterator(WizardType.EXTISTING);
}
private JavaFXProjectWizardIterator(WizardType type) {
this.type = type;
}
private WizardDescriptor.Panel[] createPanels() {
switch (type) {
case EXTISTING:
return new WizardDescriptor.Panel[]{
new PanelConfigureProject(type),
new PanelSourceFolders.Panel(),
new PanelIncludesExcludes(),};
default:
return new WizardDescriptor.Panel[]{
new PanelConfigureProject(type)
};
}
}
private String[] createSteps() {
switch (type) {
case EXTISTING:
return new String[]{
NbBundle.getMessage(JavaFXProjectWizardIterator.class, "LAB_ConfigureProject"), // NOI18N
NbBundle.getMessage(JavaFXProjectWizardIterator.class, "LAB_ConfigureSourceRoots"), // NOI18N
NbBundle.getMessage(JavaFXProjectWizardIterator.class, "LAB_PanelIncludesExcludes"),}; // NOI18N
default:
return new String[]{
NbBundle.getMessage(JavaFXProjectWizardIterator.class, "LAB_ConfigureProject"),}; // NOI18N
}
}
@Override
public Set<?> instantiate() throws IOException {
assert false : "Cannot call this method if implements WizardDescriptor.ProgressInstantiatingIterator.";
return null;
}
@Override
public Set<FileObject> instantiate(ProgressHandle handle) throws IOException {
handle.start(5);
//handle.progress (NbBundle.getMessage (NewJ2SEProjectWizardIterator.class, "LBL_NewJ2SEProjectWizardIterator_WizardProgress_ReadingProperties"));
Set<FileObject> resultSet = new LinkedHashSet<FileObject>();
FileObject mainClassFo = null;
File dirF = (File) wiz.getProperty("projdir"); // NOI18N
if (dirF == null) {
warnIssue204880("Wizard property projdir is null."); // NOI18N
throw new IOException(); // return to wizard
}
dirF = FileUtil.normalizeFile(dirF);
String name = (String) wiz.getProperty("name"); // NOI18N
String mainClass = (String) wiz.getProperty(MAIN_CLASS); // NOI18N
String fxmlName = (String) wiz.getProperty(FXML_NAME);
String librariesDefinition = (String) wiz.getProperty(SHARED_LIBRARIES);
if (librariesDefinition != null) {
if (!librariesDefinition.endsWith(File.separator)) {
librariesDefinition += File.separatorChar;
}
librariesDefinition += SharableLibrariesUtils.DEFAULT_LIBRARIES_FILENAME;
}
String platformName = (String) wiz.getProperty(JavaFXProjectUtils.PROP_JAVA_PLATFORM_NAME);
String preloader = (String) wiz.getProperty(JavaFXProjectWizardIterator.PROP_PRELOADER_NAME);
handle.progress(NbBundle.getMessage(JavaFXProjectWizardIterator.class,
"LBL_NewJ2SEProjectWizardIterator_WizardProgress_CreatingProject"), 1); // NOI18N
AntProjectHelper projectHelper;
switch (type) {
case EXTISTING:
File[] sourceFolders = (File[]) wiz.getProperty("sourceRoot"); // NOI18N
File[] testFolders = (File[]) wiz.getProperty("testRoot"); // NOI18N
String buildScriptName = (String) wiz.getProperty("buildScriptName"); // NOI18N
projectHelper = JFXProjectGenerator.createProject(dirF, name, sourceFolders, testFolders,
MANIFEST_FILE, librariesDefinition, buildScriptName, platformName,
preloader, WizardType.EXTISTING);
EditableProperties ep = projectHelper.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
String includes = (String) wiz.getProperty(ProjectProperties.INCLUDES);
if (includes == null) {
includes = "**"; // NOI18N
}
ep.setProperty(ProjectProperties.INCLUDES, includes);
String excludes = (String) wiz.getProperty(ProjectProperties.EXCLUDES);
if (excludes == null) {
excludes = ""; // NOI18N
}
ep.setProperty(ProjectProperties.EXCLUDES, excludes);
boolean fxInSwing = (Boolean) wiz.getProperty(JFXProjectProperties.JAVAFX_SWING);
if (fxInSwing) {
ep.put(JFXProjectProperties.JAVAFX_SWING, String.valueOf(true));
}
projectHelper.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, ep);
handle.progress(2);
for (File f : sourceFolders) {
FileObject srcFo = FileUtil.toFileObject(f);
if (srcFo != null) {
resultSet.add(srcFo);
}
}
break;
default:
String manifest = null;
if (type == WizardType.APPLICATION || type == WizardType.FXML || type == WizardType.SWING) {
manifest = MANIFEST_FILE;
}
if (type == WizardType.PRELOADER) {
projectHelper = JFXProjectGenerator.createPreloaderProject(dirF, name, librariesDefinition, platformName, mainClass);
} else {
projectHelper = JFXProjectGenerator.createProject(dirF, name, mainClass, fxmlName, manifest, librariesDefinition,
platformName, preloader, type);
}
handle.progress(2);
FileObject sourcesRoot = projectHelper.getProjectDirectory().getFileObject("src"); // NOI18N
if (mainClass != null && mainClass.length() > 0) {
try {
//String sourceRoot = "src"; //(String)j2seProperties.get (J2SEProjectProperties.SRC_DIR); // NOI18N
mainClassFo = getClassFO(sourcesRoot, mainClass);
assert mainClassFo != null : "sourcesRoot: " + sourcesRoot + ", mainClass: " + mainClass; // NOI18N
// Returning FileObject of main class, will be called its preferred action
//resultSet.add(mainClassFo); postponed because of creation order
} catch (Exception x) {
ErrorManager.getDefault().notify(x);
}
}
// create additional files
if (type == WizardType.FXML) {
String pName = ""; // NOI18N
if (mainClass != null && mainClass.length() > 0) {
int lastDotIdx = mainClass.lastIndexOf('.'); // NOI18N
if (lastDotIdx != -1) {
pName = mainClass.substring(0, lastDotIdx).trim();
pName = pName.replace('.', '/'); // NOI18N
pName += '/'; // NOI18N
}
}
FileObject controller = sourcesRoot.getFileObject(pName + fxmlName + NbBundle.getMessage(ConfigureFXMLControllerPanelVisual.class, "TXT_FileNameControllerPostfix") + ".java"); // NOI18N
if (controller != null) {
resultSet.add(controller);
}
FileObject fxml = sourcesRoot.getFileObject(pName + fxmlName + ".fxml"); // NOI18N
if (fxml != null) {
resultSet.add(fxml);
}
}
}
FileObject dir = FileUtil.toFileObject(dirF);
switch (type) {
case APPLICATION:
case FXML:
case SWING:
createManifest(dir, false);
break;
case EXTISTING:
createManifest(dir, true);
break;
}
handle.progress(3);
resultSet.add(dir);
// create preloader project
handle.progress(NbBundle.getMessage(JavaFXProjectWizardIterator.class,
"LBL_NewJ2SEProjectWizardIterator_WizardProgress_Preloader"), 4); // NOI18N
if (preloader != null && preloader.length() > 0) {
preloader = preloader.trim();
File preloaderDir = new File(dirF.getParentFile().getAbsolutePath() + File.separatorChar + preloader);
FileUtil.normalizeFile(preloaderDir);
String preloaderClassName = JavaFXProjectWizardIterator.generatePreloaderClassName(preloader);
AntProjectHelper preloaderProjectHelper = JFXProjectGenerator.createPreloaderProject(preloaderDir,
preloader, librariesDefinition, platformName, preloaderClassName);
FileObject sourcesRoot = preloaderProjectHelper.getProjectDirectory().getFileObject("src"); // NOI18N
FileObject preloaderClassFo = getClassFO(sourcesRoot, generatePreloaderClassName(preloader));
resultSet.add(preloaderClassFo);
FileObject preloaderDirFO = FileUtil.toFileObject(preloaderDir);
resultSet.add(preloaderDirFO);
// dependency to preloader project
final Project[] p = new Project[] {ProjectManager.getDefault().findProject(preloaderDirFO)};
FileObject ownerSourcesRoot = projectHelper.getProjectDirectory().getFileObject("src"); // NOI18N
ProjectClassPathModifier.addProjects(p, ownerSourcesRoot, ClassPath.COMPILE);
}
// Returning FileObject of project directory.
// Project will be opened
int ind = (Integer) wiz.getProperty(PROP_NAME_INDEX);
switch (type) {
case APPLICATION:
case FXML:
WizardSettings.setNewApplicationCount(ind);
break;
case PRELOADER:
WizardSettings.setNewPreloaderCount(ind);
break;
case SWING:
WizardSettings.setNewFxSwingCount(ind);
break;
case LIBRARY:
WizardSettings.setNewLibraryCount(ind);
break;
case EXTISTING:
WizardSettings.setNewProjectCount(ind);
break;
}
handle.progress(NbBundle.getMessage(JavaFXProjectWizardIterator.class,
"LBL_NewJ2SEProjectWizardIterator_WizardProgress_PreparingToOpen"), 5); // NOI18N
dirF = (dirF != null) ? dirF.getParentFile() : null;
if (dirF != null && dirF.exists()) {
ProjectChooser.setProjectsFolder(dirF);
}
SharableLibrariesUtils.setLastProjectSharable(librariesDefinition != null);
if(mainClassFo != null) {
resultSet.add(mainClassFo);
}
return resultSet;
}
private transient int index;
private transient WizardDescriptor.Panel[] panels;
private transient WizardDescriptor wiz;
@Override
public void initialize(WizardDescriptor wiz) {
this.wiz = wiz;
index = 0;
panels = createPanels();
// Make sure list of steps is accurate.
String[] steps = createSteps();
for (int i = 0; i < panels.length; i++) {
Component c = panels[i].getComponent();
if (steps[i] == null) {
// Default step name to component name of panel.
// Mainly useful for getting the name of the target
// chooser to appear in the list of steps.
steps[i] = c.getName();
}
if (c instanceof JComponent) { // assume Swing components
JComponent jc = (JComponent) c;
// Step #.
jc.putClientProperty(WizardDescriptor.PROP_CONTENT_SELECTED_INDEX, i);
// Step name (actually the whole list for reference).
jc.putClientProperty(WizardDescriptor.PROP_CONTENT_DATA, steps);
}
}
//set the default values of the sourceRoot and the testRoot properties
this.wiz.putProperty("sourceRoot", new File[0]); // NOI18N
this.wiz.putProperty("testRoot", new File[0]); // NOI18N
}
@Override
public void uninitialize(WizardDescriptor wiz) {
if (this.wiz != null) {
this.wiz.putProperty("projdir", null); // NOI18N
this.wiz.putProperty("name", null); // NOI18N
this.wiz.putProperty(MAIN_CLASS, null); // NOI18N
switch (type) {
case EXTISTING:
this.wiz.putProperty("sourceRoot", null); // NOI18N
this.wiz.putProperty("testRoot", null); // NOI18N
}
this.wiz = null;
panels = null;
}
}
@Override
public String name() {
return NbBundle.getMessage(JavaFXProjectWizardIterator.class, "LAB_IteratorName", index + 1, panels.length); // NOI18N
}
@Override
public boolean hasNext() {
return index < panels.length - 1;
}
@Override
public boolean hasPrevious() {
return index > 0;
}
@Override
public void nextPanel() {
if (!hasNext()) {
throw new NoSuchElementException();
}
index++;
}
@Override
public void previousPanel() {
if (!hasPrevious()) {
throw new NoSuchElementException();
}
index--;
}
@Override
public WizardDescriptor.Panel current() {
return panels[index];
}
// If nothing unusual changes in the middle of the wizard, simply:
@Override
public final void addChangeListener(ChangeListener l) {
}
@Override
public final void removeChangeListener(ChangeListener l) {
}
// helper methods, finds mainclass's FileObject
private static FileObject getClassFO(FileObject sourcesRoot, String className) {
className = className.replace('.', '/'); // NOI18N
return sourcesRoot.getFileObject(className + ".java"); // NOI18N
}
static String getPackageName(String displayName) {
StringBuilder builder = new StringBuilder();
boolean firstLetter = true;
for (int i = 0; i < displayName.length(); i++) {
char c = displayName.charAt(i);
if ((!firstLetter && Character.isJavaIdentifierPart(c)) || (firstLetter && Character.isJavaIdentifierStart(c))) {
firstLetter = false;
if (Character.isUpperCase(c)) {
c = Character.toLowerCase(c);
}
builder.append(c);
}
}
return builder.length() == 0 ? NbBundle.getMessage(JavaFXProjectWizardIterator.class, "TXT_DefaultPackageName") : builder.toString(); // NOI18N
}
/**
* Create a new application manifest file with minimal initial contents.
* @param dir the directory to create it in
* @throws IOException in case of problems
*/
static void createManifest(final FileObject dir, final boolean skeepIfExists) throws IOException {
if (!skeepIfExists || dir.getFileObject(MANIFEST_FILE) == null) {
FileObject manifest = dir.createData(MANIFEST_FILE);
FileLock lock = manifest.lock();
try {
OutputStream os = manifest.getOutputStream(lock);
try {
PrintWriter pw = new PrintWriter(os);
pw.println("Manifest-Version: 1.0"); // NOI18N
pw.println("X-COMMENT: Main-Class will be added automatically by build"); // NOI18N
pw.println(); // safest to end in \n\n due to JRE parsing bug
pw.flush();
} finally {
os.close();
}
} finally {
lock.releaseLock();
}
}
}
static boolean isIllegalProjectName(final String name) {
return name.length() == 0 ||
name.indexOf('/') >= 0 || //NOI18N
name.indexOf('\\') >= 0 || //NOI18N
name.indexOf(':') >= 0 || //NOI18N
name.indexOf("\"") >= 0 || //NOI18N
name.indexOf('<') >= 0 || //NOI18N
name.indexOf('>') >= 0; //NOI18N
}
static File getCanonicalFile(File file) {
try {
return file.getCanonicalFile();
} catch (IOException e) {
return null;
}
}
// TODO there should be additional wizard page for preloader,
// class name will be taken from UI
static String generatePreloaderClassName(String preloaderProjectName) {
StringBuilder sb = new StringBuilder();
if(preloaderProjectName.matches("\\b\\d.*\\b")) { //NOI18N
sb.append(NbBundle.getMessage(PanelOptionsVisual.class, "TXT_PackageNamePrefix")); //NOI18N
sb.append(preloaderProjectName.toLowerCase().replace('-', '.')); // NOI18N
sb.append('.'); // NOI18N
//sb.append(JavaFXProjectWizardIterator.GENERATED_PRELOADER_CLASS_NAME);
sb.append(NbBundle.getMessage(PanelOptionsVisual.class, "TXT_ClassNamePrefix")); //NOI18N
sb.append(preloaderProjectName.replace('-','_').replace('.','_')); // NOI18N
} else {
sb.append(preloaderProjectName.toLowerCase().replace('-', '.')); // NOI18N
sb.append('.'); // NOI18N
//sb.append(JavaFXProjectWizardIterator.GENERATED_PRELOADER_CLASS_NAME);
sb.append(preloaderProjectName.replace('-','_').replace('.','_')); // NOI18N
}
return sb.toString();
}
private void warnIssue204880(final String msg) {
LOG.log(Level.SEVERE, msg + " (issue 204880)."); // NOI18N
Exception npe = new NullPointerException(msg + " (issue 204880)."); // NOI18N
npe.printStackTrace();
NotifyDescriptor d = new NotifyDescriptor.Message(
NbBundle.getMessage(JavaFXProjectWizardIterator.class,"WARN_Issue204880"), NotifyDescriptor.ERROR_MESSAGE); // NOI18N
DialogDisplayer.getDefault().notify(d);
}
}