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.loader;
043:
044: import java.io.CharConversionException;
045: import java.io.File;
046: import java.io.IOException;
047: import java.util.ArrayList;
048: import java.util.Arrays;
049: import java.util.Collections;
050: import java.util.HashMap;
051: import java.util.Iterator;
052: import java.util.List;
053: import java.util.Map;
054: import java.util.Set;
055: import java.util.StringTokenizer;
056: import java.util.logging.Level;
057: import java.util.logging.Logger;
058: import java.util.regex.Pattern;
059: import org.netbeans.modules.apisupport.project.CreatedModifiedFiles;
060: import org.netbeans.modules.apisupport.project.ui.wizard.BasicWizardIterator;
061: import org.openide.WizardDescriptor;
062: import org.openide.filesystems.FileObject;
063: import org.openide.filesystems.FileSystem;
064: import org.openide.filesystems.FileUtil;
065: import org.openide.loaders.DataFolder;
066: import org.openide.loaders.DataObject;
067: import org.openide.modules.SpecificationVersion;
068: import org.openide.util.NbBundle;
069: import org.openide.xml.XMLUtil;
070:
071: /**
072: * Wizard for creating new DataLoaders
073: *
074: * @author Milos Kleint
075: */
076: final class NewLoaderIterator extends BasicWizardIterator {
077:
078: private NewLoaderIterator.DataModel data;
079:
080: private NewLoaderIterator() { /* Use factory method. */
081: };
082:
083: public static NewLoaderIterator createIterator() {
084: return new NewLoaderIterator();
085: }
086:
087: public Set instantiate() throws IOException {
088: CreatedModifiedFiles cmf = data.getCreatedModifiedFiles();
089: cmf.run();
090: return getCreatedFiles(cmf, data.getProject());
091: }
092:
093: protected BasicWizardIterator.Panel[] createPanels(
094: WizardDescriptor wiz) {
095: data = new NewLoaderIterator.DataModel(wiz);
096: return new BasicWizardIterator.Panel[] {
097: new FileRecognitionPanel(wiz, data),
098: new NameAndLocationPanel(wiz, data) };
099: }
100:
101: public @Override
102: void uninitialize(WizardDescriptor wiz) {
103: super .uninitialize(wiz);
104: data = null;
105: }
106:
107: static final class DataModel extends
108: BasicWizardIterator.BasicDataModel {
109:
110: private String prefix;
111: private String iconPath;
112: private String mimeType;
113: private boolean extensionBased = true;
114: private String extension;
115: private String namespace;
116:
117: private CreatedModifiedFiles files;
118:
119: DataModel(WizardDescriptor wiz) {
120: super (wiz);
121: }
122:
123: public CreatedModifiedFiles getCreatedModifiedFiles() {
124: return files;
125: }
126:
127: public void setCreatedModifiedFiles(CreatedModifiedFiles files) {
128: this .files = files;
129: }
130:
131: public String getPrefix() {
132: return prefix;
133: }
134:
135: public void setPrefix(String prefix) {
136: this .prefix = prefix;
137: }
138:
139: public String getIconPath() {
140: return iconPath;
141: }
142:
143: public void setIconPath(String iconPath) {
144: this .iconPath = iconPath;
145: }
146:
147: public String getMimeType() {
148: return mimeType;
149: }
150:
151: public void setMimeType(String mimeType) {
152: this .mimeType = mimeType;
153: }
154:
155: public boolean isExtensionBased() {
156: return extensionBased;
157: }
158:
159: public void setExtensionBased(boolean extensionBased) {
160: this .extensionBased = extensionBased;
161: }
162:
163: public String getExtension() {
164: return extension;
165: }
166:
167: public void setExtension(String extension) {
168: this .extension = extension;
169: }
170:
171: public String getNamespace() {
172: return namespace;
173: }
174:
175: public void setNamespace(String namespace) {
176: this .namespace = namespace;
177: }
178:
179: }
180:
181: public static void generateFileChanges(DataModel model) {
182: CreatedModifiedFiles fileChanges = new CreatedModifiedFiles(
183: model.getProject());
184:
185: String namePrefix = model.getPrefix();
186: String packageName = model.getPackageName();
187: final String mime = model.getMimeType();
188: Map<String, String> replaceTokens = new HashMap<String, String>();
189: replaceTokens.put("PREFIX", namePrefix);//NOI18N
190: replaceTokens.put("PACKAGENAME", packageName);//NOI18N
191: replaceTokens.put("MIMETYPE", mime);//NOI18N
192: replaceTokens.put("EXTENSIONS", formatExtensions(model
193: .isExtensionBased(), model.getExtension(), mime));//NOI18N
194: replaceTokens.put("NAMESPACES", formatNameSpace(model
195: .isExtensionBased(), model.getNamespace(), mime));//NOI18N
196:
197: // Copy action icon
198: String origIconPath = model.getIconPath();
199: if (origIconPath != null && new File(origIconPath).exists()) {
200: String relativeIconPath = model.addCreateIconOperation(
201: fileChanges, origIconPath);
202: replaceTokens.put("IMAGESNIPPET",
203: formatImageSnippet(relativeIconPath));//NOI18N
204: replaceTokens.put("ICONPATH", relativeIconPath);//NOI18N
205: replaceTokens.put("COMMENTICON", "");//NOI18N
206:
207: } else {
208: replaceTokens.put("IMAGESNIPPET", formatImageSnippet(null)); //NOI18N
209: replaceTokens.put("ICONPATH", "SET/PATH/TO/ICON/HERE"); //NOI18N
210: replaceTokens.put("COMMENTICON", "//");//NOI18N
211: }
212:
213: // 1. create dataloader file
214: String loaderName = model.getDefaultPackagePath(namePrefix
215: + "DataLoader.java", false); // NOI18N
216: // XXX use nbresloc URL protocol rather than NewLoaderIterator.class.getResource(...):
217: FileObject template = CreatedModifiedFiles
218: .getTemplate("templateDataLoader.java");//NOI18N
219: fileChanges.add(fileChanges.createFileWithSubstitutions(
220: loaderName, template, replaceTokens));
221: String loaderInfoName = model.getDefaultPackagePath(namePrefix
222: + "DataLoaderBeanInfo.java", false); // NOI18N
223: template = CreatedModifiedFiles
224: .getTemplate("templateDataLoaderBeanInfo.java");//NOI18N
225: fileChanges.add(fileChanges.createFileWithSubstitutions(
226: loaderInfoName, template, replaceTokens));
227:
228: // 2. dataobject file
229: final boolean isEditable = Pattern
230: .matches(
231: "(application/([a-zA-Z0-9_.-])*\\+xml|text/([a-zA-Z0-9_.+-])*)", //NOI18N
232: mime);
233: if (isEditable) {
234: StringBuffer editorBuf = new StringBuffer();
235: editorBuf
236: .append(" CookieSet cookies = getCookieSet();\n");//NOI18N
237: editorBuf
238: .append(" cookies.add((Node.Cookie) DataEditorSupport.create(this, getPrimaryEntry(), cookies));"); // NOI18N
239: replaceTokens.put("EDITOR_SUPPORT_SNIPPET", editorBuf
240: .toString());//NOI18N
241: replaceTokens.put("EDITOR_SUPPORT_IMPORT",
242: "import org.openide.text.DataEditorSupport;");//NOI18N
243: } else {
244: // ignore the editor support snippet
245: replaceTokens.put("EDITOR_SUPPORT_SNIPPET", "");//NOI18N
246: replaceTokens.put("EDITOR_SUPPORT_IMPORT", "");//NOI18N
247: }
248:
249: String doName = model.getDefaultPackagePath(namePrefix
250: + "DataObject.java", false); // NOI18N
251: template = null;
252: try {
253: SpecificationVersion current = model.getModuleInfo()
254: .getDependencyVersion("org.openide.loaders");
255: SpecificationVersion desired = new SpecificationVersion(
256: "6.0"); // NOI18N
257: if (current.compareTo(desired) >= 0) {
258: template = CreatedModifiedFiles
259: .getTemplate("templateDataObjectWithLookup.java");//NOI18N
260: }
261: } catch (IOException ex) {
262: Logger.getLogger(NewLoaderIterator.class.getName()).log(
263: Level.INFO, null, ex);
264: }
265: if (template == null) {
266: template = CreatedModifiedFiles
267: .getTemplate("templateDataObject.java");//NOI18N
268: }
269: fileChanges.add(fileChanges.createFileWithSubstitutions(doName,
270: template, replaceTokens));
271:
272: // 3. node file
273: String nodeName = model.getDefaultPackagePath(namePrefix
274: + "DataNode.java", false); // NOI18N
275: template = CreatedModifiedFiles
276: .getTemplate("templateDataNode.java");//NOI18N
277: fileChanges.add(fileChanges.createFileWithSubstitutions(
278: nodeName, template, replaceTokens));
279:
280: // 4. mimetyperesolver file
281: template = CreatedModifiedFiles
282: .getTemplate("templateresolver.xml");//NOI18N
283: fileChanges.add(fileChanges.createLayerEntry(
284: "Services/MIMEResolver/" + namePrefix + "Resolver.xml", //NOI18N
285: template, replaceTokens, NbBundle.getMessage(
286: NewLoaderIterator.class, "LBL_LoaderName",
287: namePrefix),//NOI18N
288: null));
289:
290: //5. update project.xml with dependencies
291: fileChanges.add(fileChanges
292: .addModuleDependency("org.openide.filesystems")); //NOI18N
293: fileChanges.add(fileChanges
294: .addModuleDependency("org.openide.loaders")); //NOI18N
295: fileChanges.add(fileChanges
296: .addModuleDependency("org.openide.nodes")); //NOI18N
297: fileChanges.add(fileChanges
298: .addModuleDependency("org.openide.util")); //NOI18N
299: if (isEditable) {
300: fileChanges.add(fileChanges
301: .addModuleDependency("org.openide.text")); //NOI18N
302: }
303: if (isEditable) {
304: fileChanges.add(fileChanges
305: .addModuleDependency("org.openide.windows")); //NOI18N
306: }
307:
308: // 6. update/create bundle file
309: String bundlePath = model.getDefaultPackagePath(
310: "Bundle.properties", true); // NOI18N
311: fileChanges.add(fileChanges.bundleKey(bundlePath, "LBL_"
312: + namePrefix + "_loader_name", // NOI18N
313: NbBundle.getMessage(NewLoaderIterator.class,
314: "LBL_LoaderName", namePrefix))); //NOI18N
315:
316: // 7. register manifest entry
317: boolean isXml = Pattern
318: .matches(
319: "(application/([a-zA-Z0-9_.-])*\\+xml|text/([a-zA-Z0-9_.-])*\\+xml)", //NOI18N
320: mime);
321: String installBefore = null;
322: if (isXml) {
323: installBefore = "org.openide.loaders.XMLDataObject, org.netbeans.modules.xml.core.XMLDataObject"; //NOI18N
324: }
325:
326: fileChanges.add(fileChanges.addLoaderSection(packageName
327: .replace('.', '/')
328: + "/" + namePrefix + "DataLoader", installBefore)); // NOI18N
329:
330: // 7a. create matching test registration for convenience (#73202)
331: fileChanges.add(fileChanges.addLookupRegistration(
332: "org.openide.loaders.DataLoader", packageName + '.'
333: + namePrefix + "DataLoader", true)); // NOI18N
334:
335: //8. create layerfile actions subsection
336:
337: fileChanges.add(fileChanges.layerModifications(
338: new CreatedModifiedFiles.LayerOperation() {
339: public void run(FileSystem layer)
340: throws IOException {
341: List<String> actions = new ArrayList<String>();
342: if (isEditable) {
343: actions
344: .add("org.openide.actions.OpenAction"); // NOI18N
345: }
346: actions
347: .addAll(Arrays
348: .asList(new String[] {
349: "org.openide.actions.FileSystemAction", // NOI18N
350: null,
351: "org.openide.actions.CutAction", // NOI18N
352: "org.openide.actions.CopyAction", // NOI18N
353: null,
354: "org.openide.actions.DeleteAction", // NOI18N
355: "org.openide.actions.RenameAction", // NOI18N
356: null,
357: "org.openide.actions.SaveAsTemplateAction", // NOI18N
358: null,
359: "org.openide.actions.ToolsAction", // NOI18N
360: "org.openide.actions.PropertiesAction", // NOI18N
361: }));
362: FileObject folder = FileUtil.createFolder(layer
363: .getRoot(), "Loaders/" + mime
364: + "/Actions"); // NOI18N
365: List<DataObject> kids = new ArrayList<DataObject>();
366: Iterator it = actions.iterator();
367: int i = 0;
368: while (it.hasNext()) {
369: String name = (String) it.next();
370: FileObject kid;
371: if (name != null) {
372: kid = folder.createData(name.replace(
373: '.', '-')
374: + ".instance"); // NOI18N
375: } else {
376: kid = folder.createData("sep-" + (++i)
377: + ".instance"); // NOI18N
378: kid.setAttribute("instanceClass",
379: "javax.swing.JSeparator"); // NOI18N
380: }
381: kids.add(DataObject.find(kid));
382: }
383: DataFolder.findFolder(folder).setOrder(
384: kids
385: .toArray(new DataObject[kids
386: .size()]));
387: }
388: }, Collections.<String> emptySet()));
389:
390: //9. create sample template
391: String suffix = null;
392: if (model.isExtensionBased()) {
393: suffix = "Template."
394: + getFirstExtension(model.getExtension()); // NOI18N
395: template = CreatedModifiedFiles.getTemplate("templateNew1");//NOI18N
396: } else {
397: template = CreatedModifiedFiles.getTemplate("templateNew2");//NOI18N
398: suffix = "Template.xml"; // NOI18N
399: try {
400: replaceTokens.put("NAMESPACE", XMLUtil
401: .toElementContent(model.getNamespace())); // NOI18N
402: } catch (CharConversionException ex) {
403: assert false : ex;
404: }
405: }
406: Map<String, Object> attrs = new HashMap<String, Object>();
407: attrs.put("template", true); // NOI18N
408: fileChanges.add(fileChanges.createLayerEntry("Templates/Other/"
409: + namePrefix + suffix, //NOI18N
410: template, replaceTokens, NbBundle.getMessage(
411: NewLoaderIterator.class,
412: "LBL_fileTemplateName", namePrefix), attrs)); //NOI18N
413: model.setCreatedModifiedFiles(fileChanges);
414: }
415:
416: private static String formatExtensions(boolean isExtensionBased,
417: String ext, String mime) {
418: if (!isExtensionBased) {
419: return "";
420: }
421: StringBuffer buff = new StringBuffer();
422: StringTokenizer tokens = new StringTokenizer(ext, " ,"); // NOI18N
423: while (tokens.hasMoreTokens()) {
424: String element = tokens.nextToken().trim();
425: if (element.startsWith(".")) { // NOI18N
426: element = element.substring(1);
427: }
428: buff.append(" <ext name=\"").append(element).append(
429: "\"/>\n"); //NOI18N
430: }
431: buff.append(" <resolver mime=\"").append(mime).append(
432: "\"/>"); //NOI18N
433: return buff.toString();
434: }
435:
436: private static String getFirstExtension(String ext) {
437: StringTokenizer tokens = new StringTokenizer(ext, " ,"); // NOI18N
438: String element = "someextension"; // NOI18N
439: if (tokens.hasMoreTokens()) {
440: element = tokens.nextToken().trim();
441: if (element.startsWith(".")) { //NOI18N
442: element = element.substring(1);
443: }
444: }
445: return element;
446: }
447:
448: private static String formatNameSpace(boolean isExtensionBased,
449: String namespace, String mime) {
450: if (isExtensionBased) {
451: return "";
452: }
453: StringBuffer buff = new StringBuffer();
454: buff.append(" <ext name=\"xml\"/>\n"); //NOI18N
455: buff.append(" <resolver mime=\"").append(mime).append(
456: "\">\n"); //NOI18N
457: buff.append(" <xml-rule>\n"); // NOI18N
458: try {
459: buff.append(" <element ns=\"").append(
460: XMLUtil.toElementContent(namespace)).append(
461: "\"/>\n"); //NOI18N
462: } catch (CharConversionException ex) {
463: assert false : ex;
464: }
465: buff.append(" </xml-rule>\n"); //NOI18N
466: buff.append(" </resolver>"); //NOI18N
467: return buff.toString();
468: }
469:
470: private static String formatImageSnippet(String path) {
471: if (path == null) {
472: return "return super.getIcon(type); // TODO add a custom icon here: Utilities.loadImage(..., true)\n"; //NOI18N
473: }
474: StringBuffer buff = new StringBuffer();
475: buff
476: .append(" if (type == BeanInfo.ICON_COLOR_16x16 || type == BeanInfo.ICON_MONO_16x16) {\n"); //NOI18N
477: buff.append(" return Utilities.loadImage(\""); //NOI18N
478: buff.append(path).append("\");\n"); //NOI18N
479: buff.append(" } else {\n"); //NOI18N
480: buff.append(" return null;\n }\n"); //NOI18N
481: return buff.toString();
482: }
483:
484: }
|