/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
*
* Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common
* Development and Distribution License("CDDL") (collectively, the
* "License"). You may not use this file except in compliance with the
* License. You can obtain a copy of the License at
* http://www.netbeans.org/cddl-gplv2.html
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
* specific language governing permissions and limitations under the
* License. When distributing the software, include this License Header
* Notice in each file and include the License file at
* nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the
* License Header, with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* Contributor(s):
*
* The Original Software is NetBeans. The Initial Developer of the Original
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2008 Sun
* Microsystems, Inc. All Rights Reserved.
*
* If you wish your version of this file to be governed by only the CDDL
* or only the GPL Version 2, indicate your decision by adding
* "[Contributor] elects to include this software in this distribution
* under the [CDDL or GPL Version 2] license." If you do not indicate a
* single choice of license, a recipient has the option to distribute
* your version of this file under either the CDDL, the GPL Version 2 or
* to extend the choice of license to its licensees as provided above.
* However, if you add GPL Version 2 code and therefore, elected the GPL
* Version 2 license, then the option applies only if the new code is
* made subject to such option by the copyright holder.
*/
package org.netbeans.modules.ruby.rubyproject.templates;
import java.awt.Component;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.SourceGroup;
import org.netbeans.modules.ruby.RubyUtils;
import org.netbeans.modules.ruby.rubyproject.templates.NewRubyFileWizardIterator.Type;
import org.netbeans.spi.project.ui.templates.support.Templates;
import org.openide.ErrorManager;
import org.openide.WizardDescriptor;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
/**
* @author Petr Hrebejk
*/
public final class RubyTargetChooserPanel implements WizardDescriptor.Panel<WizardDescriptor>, ChangeListener {
private static final String FOLDER_TO_DELETE = "folderToDelete"; // NOI18N
//private final SpecificationVersion JDK_14 = new SpecificationVersion ("1.4"); //NOI18N
private final List<ChangeListener> listeners = new ArrayList<ChangeListener>();
private RubyTargetChooserPanelGUI gui;
private final WizardDescriptor.Panel<WizardDescriptor> bottomPanel;
private WizardDescriptor wizard;
private final Project project;
private final SourceGroup folders[];
private final Type type;
public RubyTargetChooserPanel( Project project, SourceGroup folders[], WizardDescriptor.Panel<WizardDescriptor> bottomPanel, Type type) {
this.project = project;
this.folders = folders;
this.bottomPanel = bottomPanel;
this.type = type;
if ( bottomPanel != null ) {
bottomPanel.addChangeListener( this );
}
}
public Component getComponent() {
if (gui == null) {
gui = new RubyTargetChooserPanelGUI(project, folders, bottomPanel == null ? null : bottomPanel.getComponent(), type);
gui.addChangeListener(this);
}
return gui;
}
public HelpCtx getHelp() {
if ( bottomPanel != null ) {
HelpCtx bottomHelp = bottomPanel.getHelp();
if ( bottomHelp != null ) {
return bottomHelp;
}
}
//XXX
return null;
}
public boolean isValid() {
if (gui == null || gui.getTargetName() == null) {
setErrorMessage( null );
return false;
}
setLocalizedErrorMessage(null);
if (type == Type.SPEC) {
if (gui.getClassName() == null || !RubyUtils.isValidConstantName(gui.getClassName())) {
setErrorMessage("ERR_RubyTargetChooser_InvalidClass"); // NOI18N
return false;
}
String msg = RubyUtils.getIdentifierWarning(gui.getClassName(), 0);
if (msg != null) {
setLocalizedErrorMessage(msg); // warning only, don't return false
}
} else if (type == Type.CLASS || type == Type.MODULE ||
type == Type.TEST) {
if (type == Type.CLASS || type == Type.TEST) {
if (gui.getClassName() == null || !RubyUtils.isValidConstantName(gui.getClassName())) {
setErrorMessage("ERR_RubyTargetChooser_InvalidClass"); // NOI18N
return false;
}
String msg = RubyUtils.getIdentifierWarning(gui.getClassName(), 0);
if (msg != null) {
setLocalizedErrorMessage(msg); // warning only, don't return false
}
String superclass = gui.getExtends();
if (superclass != null && superclass.length() > 0) {
String[] mods = superclass.split("::"); // NOI18N
for (String mod : mods) {
if (!RubyUtils.isValidConstantName(mod)) {
setErrorMessage("ERR_RubyTargetChooser_InvalidSuperclass"); // NOI18N
return false;
}
msg = RubyUtils.getIdentifierWarning(mod, 0);
if (msg != null) {
setLocalizedErrorMessage(msg); // warning only, don't return false
}
}
}
}
if (type == Type.MODULE) {
if (gui.getClassName() == null || !RubyUtils.isValidConstantName(gui.getClassName())) {
setErrorMessage("ERR_RubyTargetChooser_InvalidModule"); // NOI18N
return false;
}
String msg = RubyUtils.getIdentifierWarning(gui.getClassName(), 0);
if (msg != null) {
setLocalizedErrorMessage(msg); // warning only, don't return false
}
}
String in = gui.getModuleName();
if (in != null && in.length() > 0) {
String[] mods = in.split("::"); // NOI18N
for (String mod : mods) {
if (!RubyUtils.isValidConstantName(mod)) {
setErrorMessage("ERR_RubyTargetChooser_InvalidInModule"); // NOI18N
return false;
}
String msg = RubyUtils.getIdentifierWarning(mod, 0);
if (msg != null) {
setLocalizedErrorMessage(msg); // warning only, don't return false
}
}
}
}
if (!isValidFileName(gui.getTargetName())) {
setErrorMessage( "ERR_RubyTargetChooser_InvalidFilename" ); // NOI18N
return false;
}
// check if the file name can be created
FileObject template = Templates.getTemplate( wizard );
boolean returnValue=true;
String errorMessage = canUseFileName (gui.getTargetFolder(), gui.getTargetName(), template.getExt ());
if (gui != null && errorMessage != null) {
setLocalizedErrorMessage (errorMessage);
}
if (errorMessage!=null) {
returnValue = false;
}
// this enables to display error messages from the bottom panel
// Nevertheless, the previous error messages have bigger priorities
if (returnValue && bottomPanel != null && !bottomPanel.isValid()) {
return false;
}
return returnValue;
}
public void addChangeListener(ChangeListener l) {
listeners.add(l);
}
public void removeChangeListener(ChangeListener l) {
listeners.remove(l);
}
private void fireChange() {
ChangeEvent e = new ChangeEvent(this);
Iterator it = listeners.iterator();
while (it.hasNext()) {
((ChangeListener)it.next()).stateChanged(e);
}
}
public void readSettings(WizardDescriptor settings ) {
wizard = settings;
if ( gui != null ) {
// Try to preselect a folder
FileObject preselectedFolder = Templates.getTargetFolder( wizard );
// Init values
gui.initValues( Templates.getTemplate( wizard ), preselectedFolder );
}
if ( bottomPanel != null ) {
bottomPanel.readSettings( settings );
}
// XXX hack, TemplateWizard in final setTemplateImpl() forces new wizard's title
// this name is used in NewFileWizard to modify the title
if (gui != null) {
Object substitute = gui.getClientProperty ("NewFileWizard_Title"); // NOI18N
if (substitute != null) {
wizard.putProperty ("NewFileWizard_Title", substitute); // NOI18N
}
}
}
private String pathToSpecHelper(WizardDescriptor wizard) {
FileObject specHelper = project.getProjectDirectory().getFileObject("spec/spec_helper.rb");//NOI18N
if (specHelper == null) {
return null;
}
FileObject targetFolder = getTargetFolderFromGUI(wizard);
if (!FileUtil.isParentOf(specHelper.getParent(), targetFolder)
&& !specHelper.getParent().equals(targetFolder)) {
return null;
}
String path = "/"; //NOI18N
FileObject parent = targetFolder;
while (!parent.equals(specHelper.getParent())) {
path += "../"; //NOI18N
parent = parent.getParent();
}
return "File.expand_path(File.dirname(__FILE__) + '" + path + specHelper.getName() + "')"; //NOI18N
}
public void storeSettings(WizardDescriptor settings) {
Object value = settings.getValue();
if (WizardDescriptor.PREVIOUS_OPTION.equals(value) || WizardDescriptor.CANCEL_OPTION.equals(value) ||
WizardDescriptor.CLOSED_OPTION.equals(value)) {
return;
}
if( isValid() ) {
if ( bottomPanel != null ) {
bottomPanel.storeSettings( settings );
}
Templates.setTargetFolder(settings, getTargetFolderFromGUI(settings));
Templates.setTargetName(settings, gui.getTargetName());
if (type == Type.SPEC) {
wizard.putProperty("classname", gui.getClassName()); // NOI18N
String name = RubyUtils.camelToUnderlinedName(gui.getClassName());
String pathToRequire = pathToSpecHelper(wizard);
if (pathToRequire == null) {
pathToRequire = "'" + name + "'";
}
wizard.putProperty("classfile", name); // NOI18N
// file_to_require includes quoting, classfile not (storing it
// for users to use).
wizard.putProperty("file_to_require", pathToRequire); // NOI18N
wizard.putProperty("classfield", name); // NOI18N
} else if (type == Type.CLASS ||
type == Type.TEST) {
wizard.putProperty("class", gui.getClassName()); // NOI18N
String name = RubyUtils.camelToUnderlinedName(gui.getClassName());
if (name.startsWith("test_")) {
name = name.substring("test_".length());
} else if (name.endsWith("_test")) {
name = name.substring(0, name.length()-"_test".length());
}
wizard.putProperty("classfile", name); // NOI18N
wizard.putProperty("module", gui.getModuleName()); // NOI18N
wizard.putProperty("extend", gui.getExtends()); // NOI18N
} else if (type == Type.MODULE) {
// NOTE - even when adding a -module-, we will use the "class" textfield
// to represent the name of the module, and the "module" text field to represent
// modules surrounding the current module
wizard.putProperty("module", gui.getClassName()); // NOI18N
wizard.putProperty("outermodules", gui.getModuleName()); // NOI18N
}
}
settings.putProperty ("NewFileWizard_Title", null); // NOI18N
if (WizardDescriptor.FINISH_OPTION.equals(value)) {
wizard.putProperty(FOLDER_TO_DELETE, null);
}
}
public void stateChanged(ChangeEvent e) {
fireChange();
}
// Private methods ---------------------------------------------------------
private void setErrorMessage( String key ) {
if ( key == null ) {
setLocalizedErrorMessage ( "" ); // NOI18N
}
else {
setLocalizedErrorMessage ( NbBundle.getMessage( RubyTargetChooserPanelGUI.class, key) ); // NOI18N
}
}
private void setLocalizedErrorMessage (String message) {
wizard.putProperty (WizardDescriptor.PROP_ERROR_MESSAGE, message); // NOI18N
}
private FileObject getTargetFolderFromGUI (WizardDescriptor wd) {
assert gui != null;
File file = new File(gui.getTargetFolder());
FileObject folder = FileUtil.toFileObject(file);
if ( folder == null ) {
try {
folder = FileUtil.createFolder(file);
// folder = rootFolder;
// StringTokenizer tk = new StringTokenizer (packageFileName,"/"); //NOI18N
// String name = null;
// while (tk.hasMoreTokens()) {
// name = tk.nextToken();
// FileObject fo = folder.getFileObject (name,""); //NOI18N
// if (fo == null) {
// break;
// }
// folder = fo;
// }
// folder = folder.createFolder(name);
// FileObject toDelete = (FileObject) wd.getProperty(FOLDER_TO_DELETE);
// if (toDelete == null) {
// wd.putProperty(FOLDER_TO_DELETE,folder);
// }
// else if (!toDelete.equals(folder)) {
// toDelete.delete();
// wd.putProperty(FOLDER_TO_DELETE,folder);
// }
// while (tk.hasMoreTokens()) {
// name = tk.nextToken();
// folder = folder.createFolder(name);
// }
}
catch( IOException e ) {
ErrorManager.getDefault().notify( ErrorManager.INFORMATIONAL, e );
}
}
return folder;
}
// Nice copy of useful methods (Taken from JavaModule)
static boolean isValidPackageName(String str) {
if (str.length() > 0 && str.charAt(0) == '.') {
return false;
}
StringTokenizer tukac = new StringTokenizer(str, "."); // NOI18N
while (tukac.hasMoreTokens()) {
String token = tukac.nextToken();
if ("".equals(token)) {
return false;
}
if (!Utilities.isJavaIdentifier(token)) {
return false;
}
}
return true;
}
static boolean isValidTypeIdentifier(String ident) {
return Utilities.isJavaIdentifier(ident);
}
static boolean isValidFileName(String ident) {
if (ident == null || ident.length() == 0) {
return false;
}
// TODO - do I want to filter filenames down to anything?
return true;
}
// helper methods copied from project/ui/ProjectUtilities
/** Checks if the given file name can be created in the target folder.
*
* @param targetFolder target folder (e.g. source group)
* @param folderName name of the folder relative to target folder
* @param newObjectName name of created file
* @param extension extension of created file
* @return localized error message or null if all right
*/
public static String canUseFileName(String folderName, String newObjectName, String extension) {
String newObjectNameToDisplay = newObjectName;
if (newObjectName != null) {
newObjectName = newObjectName.replace ('.', '/'); // NOI18N
}
if (extension != null && extension.length () > 0) {
StringBuffer sb = new StringBuffer ();
sb.append (newObjectName);
sb.append ('.'); // NOI18N
sb.append (extension);
newObjectName = sb.toString ();
}
if (extension != null && extension.length () > 0) {
StringBuffer sb = new StringBuffer ();
sb.append (newObjectNameToDisplay);
sb.append ('.'); // NOI18N
sb.append (extension);
newObjectNameToDisplay = sb.toString ();
}
// test whether the selected folder on selected filesystem already exists
if (folderName == null) {
return NbBundle.getMessage (RubyTargetChooserPanel.class, "MSG_fs_or_folder_does_not_exist"); // NOI18N
}
// target filesystem should be writable
FileObject targetFolder = FileUtil.toFileObject(new File(folderName));
if (targetFolder == null) {
return NbBundle.getMessage (RubyTargetChooserPanel.class, "MSG_fs_or_folder_does_not_exist"); // NOI18N
}
if (!targetFolder.canWrite()) {
return NbBundle.getMessage (RubyTargetChooserPanel.class, "MSG_fs_is_readonly"); // NOI18N
}
if (existFileName(targetFolder, "/" + newObjectName)) {
return NbBundle.getMessage (RubyTargetChooserPanel.class, "MSG_file_already_exist", newObjectNameToDisplay); // NOI18N
}
// all ok
return null;
}
private static boolean existFileName(FileObject targetFolder, String relFileName) {
boolean result = false;
File fileForTargetFolder = FileUtil.toFile(targetFolder);
if (fileForTargetFolder.exists()) {
result = new File (fileForTargetFolder, relFileName).exists();
} else {
result = targetFolder.getFileObject (relFileName) != null;
}
return result;
}
}