001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: // <RAVE> Copy from projects/projectui/src/org/netbeans/modules/project/ui
043: package org.netbeans.modules.visualweb.project.jsf.ui;
044:
045: import java.awt.Component;
046: import java.io.IOException;
047: import java.util.ArrayList;
048: import java.util.Iterator;
049: import java.util.List;
050: import javax.swing.event.ChangeEvent;
051: import javax.swing.event.ChangeListener;
052: import org.netbeans.api.project.Project;
053: import org.netbeans.api.project.SourceGroup;
054: import org.netbeans.spi.project.ui.templates.support.Templates;
055: import org.openide.WizardDescriptor;
056: import org.openide.filesystems.FileObject;
057: import org.openide.filesystems.FileUtil;
058: import org.openide.util.HelpCtx;
059: import org.openide.util.NbBundle; // <RAVE>
060: import java.io.File;
061: import java.util.Enumeration;
062: import org.netbeans.modules.visualweb.project.jsf.api.JsfProjectUtils;
063: import org.netbeans.modules.visualweb.project.jsf.api.JsfProjectConstants;
064: import org.netbeans.api.project.libraries.LibraryManager;
065: import org.netbeans.api.java.classpath.ClassPath;
066:
067: // </RAVE>
068:
069: /**
070: *
071: * @author Petr Hrebejk
072: */
073: final class SimpleTargetChooserPanel implements WizardDescriptor.Panel,
074: ChangeListener {
075:
076: private final List/*<ChangeListener>*/listeners = new ArrayList();
077: private SimpleTargetChooserPanelGUI gui;
078:
079: private Project project;
080: private SourceGroup[] folders;
081: private WizardDescriptor.Panel bottomPanel;
082: private WizardDescriptor wizard;
083: private boolean isFolder;
084: private String fileType;
085:
086: SimpleTargetChooserPanel(Project project, SourceGroup[] folders,
087: WizardDescriptor.Panel bottomPanel, boolean isFolder,
088: String fileType) {
089: this .folders = folders;
090: this .project = project;
091: this .bottomPanel = bottomPanel;
092: if (bottomPanel != null) {
093: bottomPanel.addChangeListener(this );
094: }
095: this .isFolder = isFolder;
096: this .fileType = fileType;
097: this .gui = null;
098: }
099:
100: public Component getComponent() {
101: if (gui == null) {
102: gui = new SimpleTargetChooserPanelGUI(project, folders,
103: bottomPanel == null ? null : bottomPanel
104: .getComponent(), isFolder);
105: gui.addChangeListener(this );
106: }
107: return gui;
108: }
109:
110: public HelpCtx getHelp() {
111: if (bottomPanel != null) {
112: HelpCtx bottomHelp = bottomPanel.getHelp();
113: if (bottomHelp != null) {
114: return bottomHelp;
115: }
116: }
117:
118: //XXX
119: return null;
120:
121: }
122:
123: public boolean isValid() {
124: boolean ok = (gui != null && gui.getTargetName() != null && (bottomPanel == null || bottomPanel
125: .isValid()));
126:
127: if (!ok) {
128: return false;
129: }
130:
131: // check if the file name can be created
132: FileObject template = Templates.getTemplate(wizard);
133:
134: String errorMessage = canUseFileName(gui.getTargetGroup()
135: .getRootFolder(), gui.getTargetFolder(), gui
136: .getTargetName(), template.getExt(), isFolder);
137: wizard.putProperty("WizardPanel_errorMessage", errorMessage); // NOI18N
138:
139: // <RAVE>
140: // return errorMessage == null;
141: if (errorMessage != null) {
142: return false;
143: }
144:
145: // no support for non-web project
146: if (!JsfProjectUtils.isWebProject(project)) {
147: wizard.putProperty("WizardPanel_errorMessage", NbBundle
148: .getMessage(SimpleTargetChooserPanel.class,
149: "MSG_NotInWebProject")); // NOI18N
150: return false;
151: }
152:
153: // no support for saving project properties
154: if (!JsfProjectUtils.supportProjectProperty(project)) {
155: wizard.putProperty("WizardPanel_errorMessage", NbBundle
156: .getMessage(SimpleTargetChooserPanel.class,
157: "MSG_NotSupportProperties")); // NOI18N
158: return false;
159: }
160:
161: // Check to make sure that the target name is not illegal
162: String targetName = gui.getTargetName();
163: if (!JsfProjectUtils.isValidJavaFileName(targetName)) {
164: wizard.putProperty("WizardPanel_errorMessage", NbBundle
165: .getMessage(SimpleTargetChooserPanel.class,
166: "MSG_InvalidJavaFileName", targetName)); // NOI18N
167: return false;
168: }
169:
170: // Check to make sure there is valid Source Package Folder
171: if (JsfProjectUtils.getSourceRoot(project) == null) {
172: wizard.putProperty("WizardPanel_errorMessage", NbBundle
173: .getMessage(SimpleTargetChooserPanel.class,
174: "MSG_NoSourceRoot")); // NOI18N
175: return false;
176: }
177:
178: // Check whether the Visual Web JSF Backwards Compatibility Kit is needed
179: String kitMesg = checkBackwardsKit();
180: if (kitMesg != null) {
181: wizard.putProperty("WizardPanel_errorMessage", kitMesg);
182: return false;
183: }
184:
185: // Extra checking that is dependent on the file type
186: if (fileType.equals(PageIterator.FILETYPE_WEBFORM)) {
187: return checkWebForm(targetName);
188: } else if (fileType.equals(PageIterator.FILETYPE_BEAN)) {
189: return checkBean(targetName);
190: }
191:
192: return true;
193: }
194:
195: private String checkBackwardsKit() {
196: LibraryManager libManager = LibraryManager.getDefault();
197: boolean JavaEE5Project = JsfProjectUtils
198: .isJavaEE5Project(project);
199: ClassPath cp = ClassPath.getClassPath(JsfProjectUtils
200: .getDocumentRoot(project), ClassPath.COMPILE);
201: boolean addJSF11 = false;
202: boolean addJAXRPC = false;
203: boolean addRowset = false;
204:
205: // It's a VisualWeb/Creator J2EE 1.4 project
206: if (!JavaEE5Project) {
207: if ((libManager.getLibrary("jsf1102") == null)
208: && // I18N
209: (cp
210: .findResource("javax/faces/FacesException.class") == null)
211: && //NOI18N
212: (cp
213: .findResource("org/apache/myfaces/webapp/StartupServletContextListener.class") == null)) { //NOI18N
214: // Both the IDE and Server do not have the JSF 1.1 RI support
215: addJSF11 = true;
216: }
217:
218: if ((libManager.getLibrary("jaxrpc16") == null) && // I18N
219: (cp.findResource("javax/xml/rpc/Service.class") == null)) { //NOI18N
220: // Both the IDE and Server do not have the JAXRPC support
221: addJAXRPC = true;
222: }
223: }
224:
225: // It's a VisualWeb/Creator J2SE 1.3/1.4 project
226: String srcLevel = JsfProjectUtils.getSourceLevel(project);
227: if (("1.3".equals(srcLevel) || "1.4".equals(srcLevel))) { // NOI18N
228: if ((libManager.getLibrary("rowset-ri") == null) && // NOI18N
229: (cp
230: .findResource("javax/sql/rowset/BaseRowSet.class") == null)) { //NOI18N
231: // IDE doesn't have the Rowset RI support
232: addRowset = true;
233: }
234: }
235:
236: if (addJSF11 || addJAXRPC || addRowset) {
237: return JsfProjectUtils.getBackwardsKitMesg(addJSF11,
238: addJAXRPC, addRowset);
239: } else {
240: return null;
241: }
242: }
243:
244: private String getFolderPath(String targetPath) {
245: // Get the path of the target folder relative to the target root
246: FileObject rootDir = gui.getTargetGroup().getRootFolder();
247: String rootPath = FileUtil.getFileDisplayName(rootDir).replace(
248: File.separatorChar, '/');
249: String folderName = gui.getTargetFolder();
250: String folderPath = folderName != null ? (rootPath
251: + (folderName.startsWith("/") ? "" : "/") + folderName)
252: : rootPath; // NOI18N
253: boolean isUnderTargetRoot = false;
254: String relativePath = null;
255: targetPath = targetPath.replace(File.separatorChar, '/');
256: if (folderPath.startsWith(targetPath)) {
257: relativePath = folderPath.substring(targetPath.length());
258: if (relativePath.equals("") || relativePath.equals("/")) {
259: isUnderTargetRoot = true;
260: } else if (relativePath.startsWith("/")) {
261: relativePath += "/"; // NOI18N
262: isUnderTargetRoot = true;
263: }
264: }
265:
266: if (!isUnderTargetRoot) {
267: wizard.putProperty("WizardPanel_errorMessage",
268: NbBundle
269: .getMessage(SimpleTargetChooserPanel.class,
270: "MSG_NotUnderTargetFolder",
271: folderPath.length() > rootPath
272: .length() ? folderPath
273: .substring(rootPath
274: .length() + 1)
275: : folderPath, targetPath
276: .length() > rootPath
277: .length() ? targetPath
278: .substring(rootPath
279: .length() + 1)
280: : targetPath)); // NOI18N
281: return null;
282: }
283:
284: // 5087626 Don't allow pages to be created under illegal subfolder
285: String[] folderTokens = relativePath.split("/");
286: for (int i = 0; i < folderTokens.length; i++) {
287: String token = folderTokens[i];
288: if (!"".equals(token)
289: && !JsfProjectUtils.isValidJavaFileName(token)) {
290: wizard.putProperty("WizardPanel_errorMessage", NbBundle
291: .getMessage(SimpleTargetChooserPanel.class,
292: "MSG_InvalidJavaFolderName", token)); // NOI18N
293: return null;
294: }
295: }
296:
297: return relativePath;
298: }
299:
300: private boolean checkWebForm(String targetName) {
301: if (JsfProjectUtils.getPortletSupport(project) != null) {
302: wizard.putProperty("WizardPanel_errorMessage", NbBundle
303: .getMessage(SimpleTargetChooserPanel.class,
304: "MSG_PortletIncompatible"));
305: return false;
306: }
307:
308: FileObject docRoot = JsfProjectUtils.getDocumentRoot(project);
309: String folderPath = getFolderPath(FileUtil
310: .getFileDisplayName(docRoot));
311: if (folderPath == null) {
312: return false;
313: }
314:
315: // 5046660/6345517 Don't allow pages to be created under here
316: // XXX actually '-' already is not a legal Java identifier
317: if (folderPath.indexOf("WEB-INF") != -1) { // NOI18N
318: wizard.putProperty("WizardPanel_errorMessage", NbBundle
319: .getMessage(SimpleTargetChooserPanel.class,
320: "MSG_InvalidTargetFolder", folderPath));
321: return false;
322: }
323:
324: // Check to make sure that the backing file doesn't already exist.
325: String jspName = targetName + ".jsp";
326: String javaName = targetName + ".java";
327: FileObject javaDir = JsfProjectUtils.getPageBeanRoot(project);
328: String javaPath = folderPath + javaName;
329: if (javaPath.startsWith("/")) {
330: javaPath = javaPath.substring(1);
331: }
332: if (javaDir.getFileObject(javaPath) != null) {
333: wizard.putProperty("WizardPanel_errorMessage", NbBundle
334: .getMessage(SimpleTargetChooserPanel.class,
335: "MSG_PageBeanNameConflict", javaName,
336: jspName)); // NOI18N
337: return false;
338: }
339:
340: // Bug 5058134: Warn if page or bean file name differs from existing file by letter case
341: FileObject folderDir = docRoot.getFileObject(folderPath);
342: FileObject srcDir = javaDir.getFileObject(folderPath);
343: if (((folderDir != null) && checkCaseInsensitiveName(folderDir,
344: targetName, "jsp"))
345: || ((srcDir != null) && checkCaseInsensitiveName(
346: srcDir, targetName, "java"))) { // NOI18N
347: wizard.putProperty("WizardPanel_errorMessage", NbBundle
348: .getMessage(SimpleTargetChooserPanel.class,
349: "MSG_FileDifferentByCase", targetName));
350: }
351:
352: return true;
353: }
354:
355: private boolean checkBean(String targetName) {
356: FileObject javaDir;
357: String beanPath;
358: String bean = (String) wizard
359: .getProperty(JsfProjectConstants.PROP_JSF_PAGEBEAN_PACKAGE);
360: if (bean != null) {
361: bean = bean.replace('.', File.separatorChar);
362: javaDir = JsfProjectUtils.getSourceRoot(project);
363: beanPath = FileUtil.getFileDisplayName(javaDir)
364: + File.separatorChar + bean;
365: } else {
366: javaDir = JsfProjectUtils.getPageBeanRoot(project);
367: beanPath = FileUtil.getFileDisplayName(javaDir);
368: }
369: String folderPath = getFolderPath(beanPath);
370: if (folderPath == null) {
371: return false;
372: }
373:
374: // Bug 5058134: Warn if page or bean file name differs from existing file by letter case
375: FileObject srcDir = javaDir.getFileObject(folderPath);
376: if ((srcDir != null)
377: && checkCaseInsensitiveName(srcDir, targetName, "java")) { // NOI18N
378: wizard.putProperty("WizardPanel_errorMessage", NbBundle
379: .getMessage(SimpleTargetChooserPanel.class,
380: "MSG_FileDifferentByCase", targetName));
381: }
382:
383: return true;
384: }
385:
386: static boolean checkCaseInsensitiveName(FileObject folder,
387: String targetName, String extension) {
388: // bugfix #41277, check only direct children
389: Enumeration children = folder.getChildren(false);
390: FileObject fo;
391: while (children.hasMoreElements()) {
392: fo = (FileObject) children.nextElement();
393: if (extension.equalsIgnoreCase(fo.getExt())
394: && targetName.equalsIgnoreCase(fo.getName())) {
395: return true;
396: }
397: }
398: return false;
399: }
400:
401: // </RAVE>
402:
403: // <RAVE> Copy from projects/projectui/src/org/netbeans/modules/project/ui/ProjectUtilities
404: /** Checks if the given file name can be created in the target folder.
405: *
406: * @param targetFolder target folder (e.g. source group)
407: * @param folderName name of the folder relative to target folder (null or /-separated)
408: * @param newObjectName name of created file
409: * @param extension extension of created file
410: * @param allowFileSeparator if '/' (and possibly other file separator, see {@link FileUtil#createFolder FileUtil#createFolder})
411: * is allowed in the newObjectName
412: * @return localized error message or null if all right
413: */
414: public static String canUseFileName(FileObject targetFolder,
415: String folderName, String newObjectName, String extension,
416: boolean allowFileSeparator) {
417: assert newObjectName != null; // SimpleTargetChooserPanel.isValid returns false if it is... XXX should it use an error label instead?
418:
419: boolean allowSlash = false;
420: boolean allowBackslash = false;
421: int errorVariant = 0;
422:
423: if (allowFileSeparator) {
424: if (File.separatorChar == '\\') {
425: errorVariant = 3;
426: allowSlash = allowBackslash = true;
427: } else {
428: errorVariant = 1;
429: allowSlash = true;
430: }
431: }
432:
433: if ((!allowSlash && newObjectName.indexOf('/') != -1)
434: || (!allowBackslash && newObjectName.indexOf('\\') != -1)) {
435: //if errorVariant == 3, the test above should never be true:
436: assert errorVariant == 0 || errorVariant == 1 : "Invalid error variant: "
437: + errorVariant;
438:
439: return NbBundle.getMessage(SimpleTargetChooserPanel.class,
440: "MSG_not_valid_filename", newObjectName,
441: new Integer(errorVariant));
442: }
443:
444: // test whether the selected folder on selected filesystem already exists
445: if (targetFolder == null) {
446: return NbBundle.getMessage(SimpleTargetChooserPanel.class,
447: "MSG_fs_or_folder_does_not_exist"); // NOI18N
448: }
449:
450: // target filesystem should be writable
451: if (!targetFolder.canWrite()) {
452: return NbBundle.getMessage(SimpleTargetChooserPanel.class,
453: "MSG_fs_is_readonly"); // NOI18N
454: }
455:
456: // file should not already exist
457: StringBuffer relFileName = new StringBuffer();
458: if (folderName != null) {
459: if (!allowBackslash && folderName.indexOf('\\') != -1) {
460: return NbBundle.getMessage(
461: SimpleTargetChooserPanel.class,
462: "MSG_not_valid_folder", folderName,
463: new Integer(1));
464: }
465: relFileName.append(folderName);
466: relFileName.append('/');
467: }
468: relFileName.append(newObjectName);
469: if (extension != null) {
470: relFileName.append('.');
471: relFileName.append(extension);
472: }
473: if (targetFolder.getFileObject(relFileName.toString()) != null) {
474: return NbBundle.getMessage(SimpleTargetChooserPanel.class,
475: "MSG_file_already_exist", newObjectName); // NOI18N
476: }
477:
478: // all ok
479: return null;
480: }
481:
482: // </RAVE>
483:
484: public synchronized void addChangeListener(ChangeListener l) {
485: listeners.add(l);
486: }
487:
488: public synchronized void removeChangeListener(ChangeListener l) {
489: listeners.remove(l);
490: }
491:
492: private void fireChange() {
493: ChangeEvent e = new ChangeEvent(this );
494: List templist;
495: synchronized (this ) {
496: templist = new ArrayList(listeners);
497: }
498: Iterator it = templist.iterator();
499: while (it.hasNext()) {
500: ((ChangeListener) it.next()).stateChanged(e);
501: }
502: }
503:
504: public void readSettings(Object settings) {
505:
506: wizard = (WizardDescriptor) settings;
507:
508: if (gui == null) {
509: getComponent();
510: }
511:
512: // Try to preselect a folder
513: FileObject preselectedTarget = Templates
514: .getTargetFolder(wizard);
515: // Try to preserve the already entered target name
516: String targetName = Templates.getTargetName(wizard);
517: // Init values
518: gui.initValues(Templates.getTemplate(wizard),
519: preselectedTarget, targetName);
520:
521: // XXX hack, TemplateWizard in final setTemplateImpl() forces new wizard's title
522: // this name is used in NewFileWizard to modify the title
523: Object substitute = gui
524: .getClientProperty("NewFileWizard_Title"); // NOI18N
525: if (substitute != null) {
526: wizard.putProperty("NewFileWizard_Title", substitute); // NOI18N
527: }
528:
529: wizard
530: .putProperty(
531: "WizardPanel_contentData",
532: new String[] { // NOI18N
533: NbBundle
534: .getBundle(
535: SimpleTargetChooserPanel.class)
536: .getString(
537: "LBL_TemplatesPanel_Name"), // NOI18N
538: NbBundle
539: .getBundle(
540: SimpleTargetChooserPanel.class)
541: .getString(
542: "LBL_SimpleTargetChooserPanel_Name") }); // NOI18N
543:
544: if (bottomPanel != null) {
545: bottomPanel.readSettings(settings);
546: }
547: }
548:
549: public void storeSettings(Object settings) {
550: if (WizardDescriptor.PREVIOUS_OPTION
551: .equals(((WizardDescriptor) settings).getValue())) {
552: return;
553: }
554: if (isValid()) {
555: if (bottomPanel != null) {
556: bottomPanel.storeSettings(settings);
557: }
558:
559: FileObject template = Templates.getTemplate(wizard);
560:
561: String name = gui.getTargetName();
562: if (name.indexOf('/') > 0) { // NOI18N
563: name = name.substring(name.lastIndexOf('/') + 1);
564: }
565:
566: Templates.setTargetFolder((WizardDescriptor) settings,
567: getTargetFolderFromGUI());
568: Templates.setTargetName((WizardDescriptor) settings, name);
569: }
570: ((WizardDescriptor) settings).putProperty(
571: "NewFileWizard_Title", null); // NOI18N
572: }
573:
574: public void stateChanged(ChangeEvent e) {
575: if (e.getSource().getClass() == PagebeanPackagePanel.class
576: && fileType.equals(PageIterator.FILETYPE_BEAN)) {
577: String bean = (String) wizard
578: .getProperty(JsfProjectConstants.PROP_JSF_PAGEBEAN_PACKAGE);
579: if (bean != null) {
580: bean = bean.replace('.', '/');
581: FileObject rootFolder = gui.getTargetGroup()
582: .getRootFolder();
583: FileObject javaDir = JsfProjectUtils
584: .getSourceRoot(project);
585: if (javaDir != null) {
586: String srcPath = FileUtil.getRelativePath(
587: rootFolder, javaDir).replace(
588: File.separatorChar, '/');
589: String beanPath = srcPath + "/" + bean;
590: String folderName = gui.getTargetFolder();
591: if (folderName != null
592: && !folderName.equals(beanPath)
593: && !folderName.startsWith(beanPath + "/")) {
594: gui.setTargetFolder(beanPath);
595: }
596: }
597: }
598: }
599: fireChange();
600: }
601:
602: private FileObject getTargetFolderFromGUI() {
603: FileObject rootFolder = gui.getTargetGroup().getRootFolder();
604: String folderName = gui.getTargetFolder();
605: String newObject = gui.getTargetName();
606:
607: if (newObject.indexOf('/') > 0) { // NOI18N
608: String path = newObject.substring(0, newObject
609: .lastIndexOf('/')); // NOI18N
610: folderName = folderName == null || "".equals(folderName) ? path
611: : folderName + '/' + path; // NOI18N
612: }
613:
614: FileObject targetFolder;
615: if (folderName == null) {
616: targetFolder = rootFolder;
617: } else {
618: targetFolder = rootFolder.getFileObject(folderName);
619: }
620:
621: if (targetFolder == null) {
622: // XXX add deletion of the file in uninitalize ow the wizard
623: try {
624: targetFolder = FileUtil.createFolder(rootFolder,
625: folderName);
626: } catch (IOException ioe) {
627: // XXX
628: // Can't create the folder
629: }
630: }
631:
632: return targetFolder;
633: }
634: }
|