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: package org.netbeans.modules.apisupport.project.ui.wizard.project;
043:
044: import java.io.IOException;
045: import java.io.InputStream;
046: import java.io.OutputStream;
047: import java.net.URL;
048: import java.util.ArrayList;
049: import java.util.Collection;
050: import java.util.Collections;
051: import java.util.HashMap;
052: import java.util.Iterator;
053: import java.util.Set;
054: import java.util.zip.ZipEntry;
055: import java.util.zip.ZipOutputStream;
056: import org.netbeans.api.project.Project;
057: import org.netbeans.api.project.ProjectUtils;
058: import org.netbeans.api.project.SourceGroup;
059: import org.netbeans.api.project.Sources;
060: import org.netbeans.api.queries.SharabilityQuery;
061: import org.netbeans.api.queries.VisibilityQuery;
062: import org.netbeans.modules.apisupport.project.CreatedModifiedFiles;
063: import org.netbeans.modules.apisupport.project.ManifestManager;
064: import org.netbeans.modules.apisupport.project.Util;
065: import org.netbeans.modules.apisupport.project.layers.LayerUtils;
066: import org.netbeans.modules.apisupport.project.spi.NbModuleProvider;
067: import org.netbeans.modules.apisupport.project.ui.wizard.BasicWizardIterator;
068: import org.openide.WizardDescriptor;
069: import org.openide.filesystems.FileLock;
070: import org.openide.filesystems.FileObject;
071: import org.openide.filesystems.FileSystem;
072: import org.openide.filesystems.FileUtil;
073:
074: /**
075: * Wizard for creating new project templates.
076: *
077: * @author Milos Kleint
078: */
079: final class NewProjectIterator extends BasicWizardIterator {
080:
081: private NewProjectIterator.DataModel data;
082:
083: public static final String[] MODULES = { "org.openide.filesystems", // NOI18N
084: "org.openide.loaders", // NOI18N
085: "org.openide.dialogs", // NOI18N
086: "org.openide.util", // NOI18N
087: "org.netbeans.modules.projectuiapi", // NOI18N
088: "org.netbeans.modules.projectapi", // NOI18N
089: "org.openide.awt" // NOI18N
090: };
091:
092: private NewProjectIterator() { /* Use factory method. */
093: };
094:
095: public static NewProjectIterator createIterator() {
096: return new NewProjectIterator();
097: }
098:
099: public Set instantiate() throws IOException {
100: CreatedModifiedFiles cmf = data.getCreatedModifiedFiles();
101: cmf.run();
102: return getCreatedFiles(cmf, data.getProject());
103: }
104:
105: protected BasicWizardIterator.Panel[] createPanels(
106: WizardDescriptor wiz) {
107: data = new NewProjectIterator.DataModel(wiz);
108: return new BasicWizardIterator.Panel[] {
109: new SelectProjectPanel(wiz, data),
110: new NameAndLocationPanel(wiz, data) };
111: }
112:
113: public @Override
114: void uninitialize(WizardDescriptor wiz) {
115: super .uninitialize(wiz);
116: data = null;
117: }
118:
119: static final class DataModel extends
120: BasicWizardIterator.BasicDataModel {
121:
122: private Project template;
123: private String name;
124: private String displayName;
125: private String category;
126:
127: private CreatedModifiedFiles files;
128:
129: DataModel(WizardDescriptor wiz) {
130: super (wiz);
131: }
132:
133: public CreatedModifiedFiles getCreatedModifiedFiles() {
134: return getFiles();
135: }
136:
137: public void setCreatedModifiedFiles(CreatedModifiedFiles files) {
138: this .setFiles(files);
139: }
140:
141: public Project getTemplate() {
142: return template;
143: }
144:
145: public void setTemplate(Project template) {
146: this .template = template;
147: }
148:
149: public String getName() {
150: return name;
151: }
152:
153: public void setName(String name) {
154: this .name = name;
155: }
156:
157: public String getDisplayName() {
158: return displayName;
159: }
160:
161: public void setDisplayName(String displayName) {
162: this .displayName = displayName;
163: }
164:
165: public String getCategory() {
166: return category;
167: }
168:
169: public void setCategory(String category) {
170: this .category = category;
171: }
172:
173: public CreatedModifiedFiles getFiles() {
174: return files;
175: }
176:
177: public void setFiles(CreatedModifiedFiles files) {
178: this .files = files;
179: }
180:
181: }
182:
183: public static void generateFileChanges(DataModel model) {
184: CreatedModifiedFiles fileChanges = new CreatedModifiedFiles(
185: model.getProject());
186: Project project = model.getProject();
187: NbModuleProvider moduleInfo = model.getModuleInfo();
188: final String category = model.getCategory();
189: final String displayName = model.getDisplayName();
190: final String name = model.getName();
191: final String packageName = model.getPackageName();
192:
193: HashMap<String, String> replaceTokens = new HashMap<String, String>();
194: replaceTokens.put("CATEGORY", category);//NOI18N
195: replaceTokens.put("DISPLAYNAME", displayName);//NOI18N
196: replaceTokens.put("TEMPLATENAME", name);//NOI18N
197: replaceTokens.put("PACKAGENAME", packageName);//NOI18N
198:
199: // 1. create project description file
200: final String descName = getRelativePath(moduleInfo
201: .getResourceDirectoryPath(false), packageName, name,
202: "Description.html"); //NOI18N
203: FileObject template = CreatedModifiedFiles
204: .getTemplate("templateDescription.html");//NOI18N
205: fileChanges.add(fileChanges.createFileWithSubstitutions(
206: descName, template, replaceTokens));
207:
208: // 2. update project dependencies
209: for (int i = 0; i < MODULES.length; i++) {
210: fileChanges
211: .add(fileChanges.addModuleDependency(MODULES[i]));
212: }
213:
214: fileChanges.add(fileChanges.bundleKeyDefaultBundle(category
215: + "/" + name + "Project.zip", displayName)); // NOI18N
216: String bundlePath = getRelativePath(moduleInfo
217: .getResourceDirectoryPath(false), packageName, "",
218: "Bundle.properties");//NOI18N
219: fileChanges.add(fileChanges.bundleKey(bundlePath,
220: "LBL_CreateProjectStep", "Name and Location")); // NOI18N
221:
222: // 3. create sample template
223: FileObject xml = LayerUtils.layerForProject(project)
224: .getLayerFile();
225: FileObject parent = xml != null ? xml.getParent() : null;
226: // XXX this is not fully accurate since if two ops would both create the same file,
227: // really the second one would automatically generate a uniquified name... but close enough!
228: Set<String> externalFiles = Collections.singleton(LayerUtils
229: .findGeneratedName(parent, name + "Project.zip")); // NOI18N
230: fileChanges.add(fileChanges.layerModifications(
231: new CreateProjectZipOperation(model.getTemplate(),
232: name, packageName, category, ManifestManager
233: .getInstance(Util
234: .getManifest(moduleInfo
235: .getManifestFile()),
236: false)), externalFiles));
237:
238: // x. generate java classes
239: final String iteratorName = getRelativePath(moduleInfo
240: .getSourceDirectoryPath(), packageName, name,
241: "WizardIterator.java"); //NOI18N
242: template = CreatedModifiedFiles
243: .getTemplate("templateWizardIterator.java");//NOI18N
244: fileChanges.add(fileChanges.createFileWithSubstitutions(
245: iteratorName, template, replaceTokens));
246: final String panelName = getRelativePath(moduleInfo
247: .getSourceDirectoryPath(), packageName, name,
248: "WizardPanel.java"); //NOI18N
249: template = CreatedModifiedFiles
250: .getTemplate("templateWizardPanel.java");//NOI18N
251: fileChanges.add(fileChanges.createFileWithSubstitutions(
252: panelName, template, replaceTokens));
253:
254: final String formName = getRelativePath(moduleInfo
255: .getSourceDirectoryPath(), packageName, name,
256: "PanelVisual.form"); //NOI18N
257: template = CreatedModifiedFiles
258: .getTemplate("templatePanelVisual.form");//NOI18N
259: fileChanges.add(fileChanges.createFileWithSubstitutions(
260: formName, template, replaceTokens));
261:
262: final String panelVisName = getRelativePath(moduleInfo
263: .getSourceDirectoryPath(), packageName, name,
264: "PanelVisual.java"); //NOI18N
265: template = CreatedModifiedFiles
266: .getTemplate("templatePanelVisual.java");//NOI18N
267: fileChanges.add(fileChanges.createFileWithSubstitutions(
268: panelVisName, template, replaceTokens));
269:
270: model.setCreatedModifiedFiles(fileChanges);
271: }
272:
273: private static String getRelativePath(String rootPath,
274: String fullyQualifiedPackageName, String prefix,
275: String postfix) {
276: StringBuffer sb = new StringBuffer();
277: sb.append(rootPath).append('/').append(
278: fullyQualifiedPackageName.replace('.', '/'))
279: .append('/').append(prefix).append(postfix);
280: return sb.toString();
281: }
282:
283: private static void createProjectZip(OutputStream target,
284: Project source) throws IOException {
285: Sources srcs = ProjectUtils.getSources(source); // #63247: don't use lookup directly
286: // assuming we got 1-sized array, should be enforced by UI.
287: SourceGroup[] grps = srcs.getSourceGroups(Sources.TYPE_GENERIC);
288: SourceGroup group = grps[0];
289: Collection<FileObject> files = new ArrayList<FileObject>();
290: collectFiles(group.getRootFolder(), files, SharabilityQuery
291: .getSharability(FileUtil.toFile(group.getRootFolder())));
292: createZipFile(target, group.getRootFolder(), files);
293: }
294:
295: private static void collectFiles(FileObject parent,
296: Collection<FileObject> accepted, int parentSharab) {
297: for (FileObject fo : parent.getChildren()) {
298: if (!VisibilityQuery.getDefault().isVisible(fo)) {
299: // #66765: ignore invisible files/folders, like CVS subdirectory
300: continue;
301: }
302: int sharab;
303: if (parentSharab == SharabilityQuery.UNKNOWN
304: || parentSharab == SharabilityQuery.MIXED) {
305: sharab = SharabilityQuery.getSharability(FileUtil
306: .toFile(fo));
307: } else {
308: sharab = parentSharab;
309: }
310: if (sharab == SharabilityQuery.NOT_SHARABLE) {
311: continue;
312: }
313: if (fo.isData() && !fo.isVirtual()) {
314: accepted.add(fo);
315: } else if (fo.isFolder()) {
316: accepted.add(fo);
317: collectFiles(fo, accepted, sharab);
318: }
319: }
320: }
321:
322: private static void createZipFile(OutputStream target,
323: FileObject root, Collection /* FileObject*/files)
324: throws IOException {
325: ZipOutputStream str = null;
326: try {
327: str = new ZipOutputStream(target);
328: Iterator it = files.iterator();
329: while (it.hasNext()) {
330: FileObject fo = (FileObject) it.next();
331: String path = FileUtil.getRelativePath(root, fo);
332: if (fo.isFolder() && !path.endsWith("/")) {
333: path = path + "/";
334: }
335: ZipEntry entry = new ZipEntry(path);
336: str.putNextEntry(entry);
337: if (fo.isData()) {
338: InputStream in = null;
339: try {
340: in = fo.getInputStream();
341: FileUtil.copy(in, str);
342: } finally {
343: if (in != null) {
344: in.close();
345: }
346: }
347: }
348: str.closeEntry();
349: }
350: } finally {
351: if (str != null) {
352: str.close();
353: }
354: }
355: }
356:
357: static class CreateProjectZipOperation implements
358: CreatedModifiedFiles.LayerOperation {
359:
360: private final String name;
361: private final String packageName;
362: private final Project templateProject;
363: private final String category;
364: private final ManifestManager manifestManager;
365:
366: public CreateProjectZipOperation(Project template, String name,
367: String packageName, String category,
368: ManifestManager manifestManager) {
369: this .packageName = packageName;
370: this .name = name;
371: this .category = category;
372: this .manifestManager = manifestManager;
373: templateProject = template;
374: }
375:
376: public void run(FileSystem layer) throws IOException {
377: FileObject folder = layer.getRoot().getFileObject(category);// NOI18N
378: if (folder == null) {
379: folder = FileUtil.createFolder(layer.getRoot(),
380: category); // NOI18N
381: }
382: FileObject file = folder
383: .createData(name + "Project", "zip"); // NOI18N
384: FileLock lock = file.lock();
385: try {
386: createProjectZip(file.getOutputStream(lock),
387: templateProject);
388: } finally {
389: lock.releaseLock();
390: }
391: String bundlePath = manifestManager.getLocalizingBundle();
392: String suffix = ".properties"; // NOI18N
393: if (bundlePath != null && bundlePath.endsWith(suffix)) {
394: bundlePath = bundlePath.substring(0,
395: bundlePath.length() - suffix.length()).replace(
396: '/', '.');
397: file.setAttribute("SystemFileSystem.localizingBundle",
398: bundlePath); // NOI18N
399: } else {
400: // XXX what?
401: }
402: file.setAttribute("template", Boolean.TRUE); // NOI18N
403: URL descURL = new URL("nbresloc:/"
404: + packageName.replace('.', '/') + "/" + name
405: + "Description.html"); // NOI18N
406: file.setAttribute("instantiatingWizardURL", descURL); // NOI18N
407: file.setAttribute("instantiatingIterator", "methodvalue:"
408: + packageName + "." + name
409: + "WizardIterator.createIterator"); // NOI18N
410: }
411: }
412:
413: }
|