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-2006 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: package org.netbeans.modules.php.project.wizards;
042:
043: import java.awt.Component;
044: import java.io.File;
045: import java.io.IOException;
046: import java.nio.charset.Charset;
047: import java.text.MessageFormat;
048: import java.util.HashSet;
049: import java.util.NoSuchElementException;
050: import java.util.Set;
051: import java.util.logging.Logger;
052: import javax.swing.JComponent;
053: import javax.swing.event.ChangeListener;
054: import org.netbeans.api.project.Project;
055: import org.netbeans.api.project.ProjectManager;
056: import org.netbeans.api.queries.FileEncodingQuery;
057: import org.netbeans.modules.php.project.PhpProject;
058: import org.netbeans.modules.php.project.PhpProjectType;
059: import org.netbeans.modules.php.rt.spi.providers.Host;
060: import org.netbeans.modules.php.rt.spi.providers.ProjectConfigProvider;
061: import org.netbeans.modules.php.rt.spi.providers.WebServerProvider;
062: import org.netbeans.modules.php.rt.utils.PhpProjectSharedConstants;
063: import org.netbeans.spi.project.support.ant.AntProjectHelper;
064: import org.netbeans.spi.project.support.ant.EditableProperties;
065: import org.netbeans.spi.project.support.ant.ProjectGenerator;
066: import org.netbeans.spi.project.ui.templates.support.Templates;
067: import org.openide.WizardDescriptor;
068: import org.openide.WizardDescriptor.InstantiatingIterator;
069: import org.openide.WizardDescriptor.Panel;
070: import org.openide.filesystems.FileObject;
071: import org.openide.filesystems.FileStateInvalidException;
072: import org.openide.filesystems.FileUtil;
073: import org.openide.loaders.DataFolder;
074: import org.openide.loaders.DataObject;
075: import org.openide.util.NbBundle;
076: import org.w3c.dom.Document;
077: import org.w3c.dom.Element;
078:
079: /**
080: * @author ads
081: *
082: */
083: public final class NewPhpProjectWizardIterator implements
084: InstantiatingIterator {
085:
086: public static final String WIZARD_PANEL_ERROR_MESSAGE = "WizardPanel_errorMessage"; // NOI18N
087: private static final String LBL_WIZARD_STEPS_COUNT = "LBL_WizardStepsCount"; // NOI18N
088: public static final String CONTENT_DATA = "WizardPanel_contentData"; // NOI18N
089: public static final String SELECTED_INDEX = "WizardPanel_contentSelectedIndex"; // NOI18N
090: // steps
091: public static final String STEP_PROJECT = "LBL_ProjectTitleName"; // NOI18N
092: public static final String STEP_WEB_SERVER = "LBL_ProjectWebServer"; // NOI18N
093: // properties
094: public static final String PROJECT_DIR = "projdir"; // NOI18N
095: public static final String NAME = PhpProjectSharedConstants.PHP_PROJECT_NAME; // NOI18N
096: public static final String SOURCE_ROOT = "sourceRoot"; // NOI18N
097: public static final String SET_AS_MAIN = "setAsMain"; // NOI18N
098: public static final String INDEX_FILE_NAME = "indexFileName"; // NOI18N
099: public static final String INDEX_FILE_CREATE = "createIndexFile"; // NOI18N
100: public static final String HOST = "host"; // NOI18N
101: public static final String COMMAND_LINE = "command"; // NOI18N
102: public static final String VERSION = "version"; // NOI18N
103: public static final String SOURCE_ENCODING = PhpProject.SOURCE_ENCODING;
104:
105: // constants
106: public static final String CURRENT_FOLDER_PATTERN = "."; // NOI18N
107:
108: /** invokes <code>this(NewPhpProjectWizardIterator.PROJECT_TYPE_NEW)<code>
109: */
110: public NewPhpProjectWizardIterator() {
111: }
112:
113: /* (non-Javadoc)
114: * @see org.openide.WizardDescriptor.InstantiatingIterator#initialize(org.openide.WizardDescriptor)
115: */
116: public void initialize(WizardDescriptor descriptor) {
117: myDescriptor = descriptor;
118:
119: String[] steps = createSteps();
120: for (int i = 0; i < myPanels.length; i++) {
121: Component component = myPanels[i].getComponent();
122: if (steps[i] == null) {
123: // Default step name to component name of panel.
124: // Mainly useful for getting the name of the target
125: // chooser to appear in the list of steps.
126: steps[i] = component.getName();
127: }
128: if (component instanceof JComponent) {
129: JComponent jComp = (JComponent) component;
130: jComp.putClientProperty(SELECTED_INDEX, i);
131: jComp.putClientProperty(CONTENT_DATA, steps);
132: }
133: }
134: }
135:
136: /* (non-Javadoc)
137: * @see org.openide.WizardDescriptor.InstantiatingIterator#instantiate()
138: */
139: public Set instantiate() throws IOException {
140: Set<Object> resultSet = new HashSet<Object>(2);
141: File dir = FileUtil.normalizeFile((File) getDescriptor()
142: .getProperty(PROJECT_DIR));
143: String name = (String) getDescriptor().getProperty(NAME);
144:
145: AntProjectHelper helper = createProject(dir, name);
146:
147: // add Project to created objects set
148: FileObject fileObject = FileUtil.toFileObject(dir);
149: resultSet.add(fileObject);
150:
151: // add sources to created objects set
152: File src = createSourceRoot(dir);
153: FileObject sourceDir = FileUtil.toFileObject(src);
154: if (sourceDir != null) {
155: resultSet.add(sourceDir);
156: }
157:
158: // add to created objects set
159: Boolean createIndexFile = (Boolean) getDescriptor()
160: .getProperty(INDEX_FILE_CREATE);
161: if (createIndexFile != null && createIndexFile.booleanValue()) {
162: FileObject template = Templates
163: .getTemplate(getDescriptor());
164: DataObject dObject = createIndexFile(template, src);
165: if (dObject != null) {
166: resultSet.add(dObject.getPrimaryFile());
167: }
168: }
169:
170: // Returning set of FileObject of project diretory.
171: // Project will be open and set as main
172: return resultSet;
173: }
174:
175: private File createSourceRoot(File projectDir) throws IOException {
176: Object obj = getDescriptor().getProperty(SOURCE_ROOT);
177: File src = null;
178: if (obj != null && obj instanceof File) {
179: src = (File) obj;
180: if (isParentOf(projectDir, src) && !src.exists()) {
181: createSourceFolder(src);
182: }
183: }
184: return src;
185: }
186:
187: private void createSourceFolder(File src) throws IOException {
188: if (!src.mkdirs()) {
189: throw new IOException("Can not create source folder."); // NOI18N
190: }
191: }
192:
193: private boolean isParentOf(File folder, File f) {
194: String folderPath = folder.getAbsolutePath();
195: String filePath = f.getAbsolutePath();
196: if (filePath.startsWith(folderPath)
197: && !filePath.equals(folderPath)) {
198: return true;
199: }
200: return false;
201: }
202:
203: /* (non-Javadoc)
204: * @see org.openide.WizardDescriptor.InstantiatingIterator#uninitialize(org.openide.WizardDescriptor)
205: */
206: public void uninitialize(WizardDescriptor descriptor) {
207: descriptor.putProperty(PROJECT_DIR, null);
208: descriptor.putProperty(NAME, null);
209: descriptor.putProperty(SOURCE_ROOT, null);
210: descriptor.putProperty(SET_AS_MAIN, null);
211: descriptor.putProperty(INDEX_FILE_NAME, null);
212: descriptor.putProperty(INDEX_FILE_CREATE, null);
213: descriptor.putProperty(HOST, null);
214: descriptor.putProperty(COMMAND_LINE, null);
215: descriptor.putProperty(VERSION, null);
216: descriptor.putProperty(SOURCE_ENCODING, null);
217:
218: myPanels = null;
219: myDescriptor = null;
220: }
221:
222: /* (non-Javadoc)
223: * @see org.openide.WizardDescriptor.Iterator#addChangeListener(javax.swing.event.ChangeListener)
224: */
225: public void addChangeListener(ChangeListener listener) {
226: }
227:
228: /* (non-Javadoc)
229: * @see org.openide.WizardDescriptor.Iterator#current()
230: */
231: public Panel current() {
232: return myPanels[myIndex];
233: }
234:
235: /* (non-Javadoc)
236: * @see org.openide.WizardDescriptor.Iterator#hasNext()
237: */
238: public boolean hasNext() {
239: return myIndex < myPanels.length - 1;
240: }
241:
242: /* (non-Javadoc)
243: * @see org.openide.WizardDescriptor.Iterator#hasPrevious()
244: */
245: public boolean hasPrevious() {
246: return myIndex > 0;
247: }
248:
249: /* (non-Javadoc)
250: * @see org.openide.WizardDescriptor.Iterator#name()
251: */
252: public String name() {
253: return MessageFormat.format(NbBundle.getBundle(
254: NewPhpProjectWizardIterator.class).getString(
255: LBL_WIZARD_STEPS_COUNT), new Object[] {
256: (myIndex + 1) + "", (myPanels.length) + "" });
257: }
258:
259: /* (non-Javadoc)
260: * @see org.openide.WizardDescriptor.Iterator#nextPanel()
261: */
262: public void nextPanel() {
263: if (!hasNext()) {
264: throw new NoSuchElementException();
265: }
266: myIndex++;
267: }
268:
269: /* (non-Javadoc)
270: * @see org.openide.WizardDescriptor.Iterator#previousPanel()
271: */
272: public void previousPanel() {
273: if (!hasPrevious()) {
274: throw new NoSuchElementException();
275: }
276: myIndex--;
277: }
278:
279: /* (non-Javadoc)
280: * @see org.openide.WizardDescriptor.Iterator#removeChangeListener(javax.swing.event.ChangeListener)
281: */
282: public void removeChangeListener(ChangeListener listener) {
283: }
284:
285: private WizardDescriptor getDescriptor() {
286: return myDescriptor;
287: }
288:
289: /**
290: * prepares name of index file to create it.
291: * Get's name using WizardDescriptor.getProperty(INDEX_FILE_NAME).
292: * If name has the same extension as the one specified in parameter,
293: * removes it.
294: *
295: * @param plannedExt extension that file will have.
296: *
297: * @returns index file name without extension
298: */
299: private String getIndexFileName(String plannedExt) {
300: String name = (String) getDescriptor().getProperty(
301: INDEX_FILE_NAME);
302: if (name == null || name.length() == 0) {
303: return null;
304: }
305: int index = name.lastIndexOf("." + plannedExt);
306: // if name doesn't contain only '.<extension>'
307: if (index > 0) {
308: return name.substring(0, index);
309: }
310: return name;
311: }
312:
313: private DataObject createIndexFile(FileObject template,
314: File sourceDir) throws IOException {
315: String indexFileName = getIndexFileName(template.getExt());
316: if (indexFileName == null) {
317: return null;
318: }
319:
320: DataFolder dataFolder = DataFolder
321: .findFolder(getSourceFileObject(sourceDir));
322: DataObject dTemplate = DataObject.find(template);
323: return dTemplate.createFromTemplate(dataFolder, indexFileName);
324: }
325:
326: private FileObject getSourceFileObject(File sourceDir)
327: throws IOException {
328: if (!sourceDir.exists()) {
329: createSourceFolder(sourceDir);
330: }
331: return FileUtil.createData(sourceDir);
332: }
333:
334: /**
335: * The actual return array has only 1 element. So this method is not needed
336: * actually. It exists for possible future change quantity of steps in wizard.
337: */
338: private String[] createSteps() {
339: myPanels = new WizardDescriptor.Panel[] {
340: new PhpProjectConfigurePanel(),
341: new ProviderSpecificPanel() };
342: String[] steps = new String[] {
343: NbBundle.getBundle(NewPhpProjectWizardIterator.class)
344: .getString(STEP_PROJECT),
345: NbBundle.getBundle(NewPhpProjectWizardIterator.class)
346: .getString(STEP_WEB_SERVER) };
347: return steps;
348: }
349:
350: private AntProjectHelper createProject(File dir, String name)
351: throws IOException {
352: refreshFileSystem(dir);
353: if (!dir.mkdirs()) {
354: throw new IOException("Can not create project folder."); // NOI18N
355: }
356: refreshFileSystem(dir);
357: AntProjectHelper helper = ProjectGenerator.createProject(
358: FileUtil.toFileObject(dir), PhpProjectType.TYPE);
359: configure(helper, name);
360: return helper;
361: }
362:
363: private void configure(AntProjectHelper helper, String name)
364: throws IOException {
365: Element data = helper.getPrimaryConfigurationData(true);
366: Document doc = data.getOwnerDocument();
367: Element nameEl = doc.createElementNS(
368: PhpProjectType.PROJECT_CONFIGURATION_NAMESPACE, NAME);
369: nameEl.appendChild(doc.createTextNode(name));
370: data.appendChild(nameEl);
371: helper.putPrimaryConfigurationData(data, true);
372:
373: EditableProperties properties = helper
374: .getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
375:
376: /*
377: * Set source roots.
378: */
379: configureSources(helper, properties);
380:
381: /*
382: * Set Encoding
383: */
384: configureEncoding(properties);
385: /*
386: * Save provider and host properties
387: */
388: configureProvider(properties);
389: /*
390: * Set version.
391: */
392: // version selection was removed from wizard. It will be available in customizer
393: //properties.setProperty(PhpProject.VERSION, getDescriptor().getProperty(VERSION).toString());
394: //String defaultVersion = NbBundle.getBundle(NewPhpProjectWizardIterator.class).getString(DEFAULT_PHP_VERSION);
395: //properties.setProperty(PhpProject.VERSION, defaultVersion);
396: helper.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH,
397: properties);
398:
399: properties = helper
400: .getProperties(AntProjectHelper.PRIVATE_PROPERTIES_PATH);
401: helper.putProperties(AntProjectHelper.PRIVATE_PROPERTIES_PATH,
402: properties);
403:
404: /* delegate server specific configuration to provider */
405: configureHost(helper);
406:
407: Project project = ProjectManager.getDefault().findProject(
408: helper.getProjectDirectory());
409: ProjectManager.getDefault().saveProject(project);
410: }
411:
412: private void configureSources(AntProjectHelper helper,
413: EditableProperties properties) {
414: String sourcePath = null;
415:
416: Object obj = getDescriptor().getProperty(SOURCE_ROOT);
417: Logger.getLogger(getClass().getName()).info(
418: ">>>>> SRC ROOT " + obj);
419: if (obj != null && obj instanceof File) {
420: File sourceDir = (File) obj;
421: File projectDir = FileUtil.toFile(helper
422: .getProjectDirectory());
423:
424: sourcePath = getRelatedSourcePath(sourceDir, projectDir);
425: // do not store anything if no reasonable src specified
426: //} else {
427: // sourcePath = NbBundle.getBundle(NewPhpProjectWizardIterator.class)
428: // .getString(DEFAULT_SOURCE_ROOT_DIR);
429: }
430: properties.setProperty(PhpProject.SRC, sourcePath);
431:
432: }
433:
434: private void configureEncoding(EditableProperties properties) {
435: String encoding = null;
436:
437: Object obj = getDescriptor().getProperty(SOURCE_ENCODING);
438: if (obj != null && obj instanceof String) {
439: encoding = (String) encoding;
440: }
441: if (encoding == null) {
442: Charset enc = FileEncodingQuery.getDefaultEncoding();
443: encoding = enc.name();
444: }
445: properties.setProperty(PhpProject.SOURCE_ENCODING, encoding);
446: }
447:
448: private String getRelatedSourcePath(File sourceFolder,
449: File projectFolder) {
450: String sourcePath = sourceFolder.getAbsolutePath();
451: String projectPath = projectFolder.getAbsolutePath();
452:
453: if (projectPath.equalsIgnoreCase(sourcePath)) {
454: return CURRENT_FOLDER_PATTERN;
455: } else if (sourcePath.startsWith(projectPath + File.separator)) {
456: String name = sourcePath
457: .substring(projectPath.length() + 1);
458: return name;
459: } else {
460: return sourcePath;
461: }
462: }
463:
464: private void configureProvider(EditableProperties properties) {
465: Object obj = getDescriptor().getProperty(HOST);
466: // if host was selected
467: if (obj != null) {
468: if (obj instanceof HostHolder) {
469: Host host = ((HostHolder) obj).getHost();
470: WebServerProvider provider = host.getProvider();
471: String provider_id = provider.getClass()
472: .getCanonicalName();
473: properties.setProperty(PhpProject.PROVIDER_ID,
474: provider_id);
475: properties.setProperty(WebServerProvider.HOST_ID, host
476: .getId());
477: }
478: // add CMD support to all projects
479: /*
480: else {
481: obj = getDescriptor().getProperty( COMMAND_LINE );
482: if ( obj instanceof String ) {
483: properties.setProperty( PhpProject.COMMAND_PATH , (String) obj );
484: }
485: }
486: */
487: }
488: }
489:
490: private void configureHost(AntProjectHelper helper) {
491: Object obj = getDescriptor().getProperty(HOST);
492: if (obj != null) {
493: if (obj instanceof HostHolder) {
494: Host host = ((HostHolder) obj).getHost();
495: ProjectConfigProvider provider = host.getProvider()
496: .getProjectConfigProvider();
497: provider.configureProject(helper, getDescriptor());
498: }
499: }
500: }
501:
502: private static void refreshFileSystem(final File dir)
503: throws FileStateInvalidException {
504: File rootF = dir;
505: while (rootF.getParentFile() != null) {
506: rootF = rootF.getParentFile();
507: }
508: FileObject dirFO = FileUtil.toFileObject(rootF);
509: assert dirFO != null : "At least disk roots must be mounted! "
510: + rootF; // NOI18N
511: dirFO.getFileSystem().refresh(false);
512: }
513:
514: private WizardDescriptor myDescriptor;
515:
516: private WizardDescriptor.Panel[] myPanels;
517:
518: private int myIndex;
519:
520: }
|