| /* |
| * 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); |
| } |
| } |