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: * Portions Copyrighted 2008 Craig MacKay.
042: */
043:
044: package org.netbeans.modules.spring.beans.wizards;
045:
046: import java.awt.Component;
047: import java.io.BufferedWriter;
048: import java.io.File;
049: import java.io.IOException;
050: import java.io.OutputStreamWriter;
051: import java.util.ArrayList;
052: import java.util.Collections;
053: import java.util.List;
054: import java.util.NoSuchElementException;
055: import java.util.Set;
056: import javax.swing.JComponent;
057: import javax.swing.event.ChangeListener;
058: import javax.swing.text.BadLocationException;
059: import org.netbeans.api.java.classpath.ClassPath;
060: import org.netbeans.api.java.project.classpath.ProjectClassPathModifier;
061: import org.netbeans.api.project.Project;
062: import org.netbeans.api.project.ProjectUtils;
063: import org.netbeans.api.project.SourceGroup;
064: import org.netbeans.api.project.Sources;
065: import org.netbeans.api.project.libraries.Library;
066: import org.netbeans.editor.BaseDocument;
067: import org.netbeans.editor.Formatter;
068: import org.netbeans.modules.j2ee.core.api.support.SourceGroups;
069: import org.netbeans.modules.spring.api.beans.ConfigFileGroup;
070: import org.netbeans.modules.spring.api.beans.ConfigFileManager;
071: import org.netbeans.modules.spring.api.beans.SpringConstants;
072: import org.netbeans.modules.spring.beans.ProjectSpringScopeProvider;
073: import org.netbeans.spi.project.ui.templates.support.Templates;
074: import org.openide.WizardDescriptor;
075: import org.openide.filesystems.FileAlreadyLockedException;
076: import org.openide.filesystems.FileLock;
077: import org.openide.filesystems.FileObject;
078: import org.openide.filesystems.FileSystem;
079: import org.openide.filesystems.FileUtil;
080: import org.openide.text.CloneableEditorSupport;
081: import org.openide.util.Exceptions;
082: import org.openide.util.Mutex.ExceptionAction;
083: import org.openide.util.MutexException;
084:
085: public final class NewSpringXMLConfigWizardIterator implements
086: WizardDescriptor.AsynchronousInstantiatingIterator {
087:
088: private int index;
089: private WizardDescriptor wizard;
090: private WizardDescriptor.Panel[] panels;
091:
092: /**
093: * Initialize panels representing individual wizard's steps and sets
094: * various properties for them influencing wizard appearance.
095: */
096: private WizardDescriptor.Panel[] getPanels() {
097: if (panels == null) {
098: Project p = Templates.getProject(wizard);
099: SourceGroup[] groups = ProjectUtils.getSources(p)
100: .getSourceGroups(Sources.TYPE_GENERIC);
101: List<ConfigFileGroup> configFileGroups = getConfigFileManager(
102: p).getConfigFileGroups();
103: SpringXMLConfigGroupPanel configGroupPanel = configFileGroups
104: .isEmpty() ? null : new SpringXMLConfigGroupPanel(
105: configFileGroups);
106: WizardDescriptor.Panel targetChooser = Templates
107: .createSimpleTargetChooser(p, groups,
108: configGroupPanel);
109:
110: panels = new WizardDescriptor.Panel[] { targetChooser,
111: new SpringXMLConfigNamespacesPanel(), };
112: String[] steps = createSteps();
113: for (int i = 0; i < panels.length; i++) {
114: Component c = panels[i].getComponent();
115: if (steps[i] == null) {
116: // Default step name to component name of panel. Mainly
117: // useful for getting the name of the target chooser to
118: // appear in the list of steps.
119: steps[i] = c.getName();
120: }
121: if (c instanceof JComponent) { // assume Swing components
122: JComponent jc = (JComponent) c;
123: // Sets step number of a component
124: jc.putClientProperty(
125: "WizardPanel_contentSelectedIndex",
126: new Integer(i)); // NOI18N
127: // Sets steps names for a panel
128: jc.putClientProperty("WizardPanel_contentData",
129: steps); // NOI18N
130: // Turn on subtitle creation on each step
131: jc.putClientProperty("WizardPanel_autoWizardStyle",
132: Boolean.TRUE); // NOI18N
133: // Show steps on the left side with the image on the background
134: jc.putClientProperty(
135: "WizardPanel_contentDisplayed",
136: Boolean.TRUE); // NOI18N
137: // Turn on numbering of all steps
138: jc.putClientProperty("WizardPanel_contentNumbered",
139: Boolean.TRUE); // NOI18N
140: }
141: }
142: }
143: return panels;
144: }
145:
146: public Set instantiate() throws IOException {
147: final FileObject targetFolder = Templates
148: .getTargetFolder(wizard);
149: final String targetName = Templates.getTargetName(wizard);
150:
151: final FileObject[] createdFile = { null };
152:
153: FileUtil.runAtomicAction(new FileSystem.AtomicAction() {
154:
155: public void run() throws IOException {
156: createdFile[0] = targetFolder.createData(targetName,
157: Templates.getTemplate(wizard).getExt());
158: String[] incNamespaces = (String[]) wizard
159: .getProperty(SpringXMLConfigNamespacesPanel.INCLUDED_NAMESPACES);
160: generateFileContents(createdFile[0], incNamespaces);
161: }
162: });
163: boolean addSpringToClassPath = (Boolean) wizard
164: .getProperty(SpringXMLConfigNamespacesPanel.ADD_SPRING_TO_CLASSPATH);
165: if (addSpringToClassPath) {
166: Library[] libraries = { (Library) wizard
167: .getProperty(SpringXMLConfigNamespacesPanel.SPRING_LIBRARY) };
168: addLibrariesToClassPath(libraries);
169: }
170:
171: @SuppressWarnings("unchecked")
172: Set<ConfigFileGroup> selectedGroups = (Set<ConfigFileGroup>) wizard
173: .getProperty(SpringXMLConfigGroupPanel.CONFIG_FILE_GROUPS);
174: addFileToConfigFileManager(
175: selectedGroups != null ? selectedGroups : Collections
176: .<ConfigFileGroup> emptySet(), FileUtil
177: .toFile(createdFile[0]));
178:
179: return Collections.singleton(createdFile[0]);
180: }
181:
182: private void addLibrariesToClassPath(Library[] libraries)
183: throws IOException {
184: FileObject artifact = getSourceGroupArtifact(Templates
185: .getProject(wizard), Templates.getTargetFolder(wizard));
186: if (artifact != null) {
187: ProjectClassPathModifier.addLibraries(libraries, artifact,
188: ClassPath.COMPILE);
189: }
190: }
191:
192: private void addFileToConfigFileManager(
193: final Set<ConfigFileGroup> selectedGroups, final File file)
194: throws IOException {
195: final ConfigFileManager manager = getConfigFileManager(Templates
196: .getProject(wizard));
197: try {
198: manager.mutex().writeAccess(new ExceptionAction<Void>() {
199: public Void run() throws IOException {
200: List<File> origFiles = manager.getConfigFiles();
201: List<File> newFiles = new ArrayList<File>(origFiles);
202: newFiles.add(file);
203: List<ConfigFileGroup> origGroups = manager
204: .getConfigFileGroups();
205: List<ConfigFileGroup> newGroups = null;
206: if (selectedGroups.size() > 0) {
207: newGroups = new ArrayList<ConfigFileGroup>(
208: origGroups.size());
209: for (ConfigFileGroup group : origGroups) {
210: if (selectedGroups.contains(group)) {
211: ConfigFileGroup newGroup = addFileToConfigGroup(
212: group, file);
213: newGroups.add(newGroup);
214: } else {
215: newGroups.add(group);
216: }
217: }
218: } else {
219: newGroups = origGroups;
220: }
221: manager
222: .putConfigFilesAndGroups(newFiles,
223: newGroups);
224: manager.save();
225: return null;
226: }
227: });
228: } catch (MutexException e) {
229: throw (IOException) e.getException();
230: }
231: }
232:
233: private ConfigFileGroup addFileToConfigGroup(ConfigFileGroup group,
234: File file) {
235: List<File> files = group.getFiles();
236: files.add(file);
237: return ConfigFileGroup.create(group.getName(), files);
238: }
239:
240: public void initialize(WizardDescriptor wizard) {
241: this .wizard = wizard;
242: }
243:
244: public void uninitialize(WizardDescriptor wizard) {
245: panels = null;
246: }
247:
248: public WizardDescriptor.Panel current() {
249: return getPanels()[index];
250: }
251:
252: public String name() {
253: return index + 1 + ". from " + getPanels().length; // NOI18N
254: }
255:
256: public boolean hasNext() {
257: return index < getPanels().length - 1;
258: }
259:
260: public boolean hasPrevious() {
261: return index > 0;
262: }
263:
264: public void nextPanel() {
265: if (!hasNext()) {
266: throw new NoSuchElementException();
267: }
268: index++;
269: }
270:
271: public void previousPanel() {
272: if (!hasPrevious()) {
273: throw new NoSuchElementException();
274: }
275: index--;
276: }
277:
278: // If nothing unusual changes in the middle of the wizard, simply:
279: public void addChangeListener(ChangeListener l) {
280: }
281:
282: public void removeChangeListener(ChangeListener l) {
283: }
284:
285: // You could safely ignore this method. Is is here to keep steps which were
286: // there before this wizard was instantiated. It should be better handled
287: // by NetBeans Wizard API itself rather than needed to be implemented by a
288: // client code.
289: private String[] createSteps() {
290: String[] beforeSteps = null;
291: Object prop = wizard.getProperty("WizardPanel_contentData"); // NOI18N
292: if (prop != null && prop instanceof String[]) {
293: beforeSteps = (String[]) prop;
294: }
295:
296: if (beforeSteps == null) {
297: beforeSteps = new String[0];
298: }
299:
300: String[] res = new String[(beforeSteps.length - 1)
301: + panels.length];
302: for (int i = 0; i < res.length; i++) {
303: if (i < (beforeSteps.length - 1)) {
304: res[i] = beforeSteps[i];
305: } else {
306: res[i] = panels[i - beforeSteps.length + 1]
307: .getComponent().getName();
308: }
309: }
310: return res;
311: }
312:
313: private void generateFileContents(final FileObject targetFile,
314: String[] incNamespaces) {
315: StringBuilder sb = generateXML(incNamespaces);
316:
317: try {
318: Class<?> kitClass = CloneableEditorSupport.getEditorKit(
319: SpringConstants.CONFIG_MIME_TYPE).getClass();
320: BaseDocument doc = new BaseDocument(kitClass, false);
321: Formatter f = Formatter.getFormatter(kitClass);
322:
323: doc.remove(0, doc.getLength());
324: doc.insertString(0, sb.toString(), null);
325: f.reformatLock();
326: try {
327: doc.atomicLock();
328: try {
329: f.reformat(doc, 0, doc.getLength());
330: } finally {
331: doc.atomicUnlock();
332: }
333: } finally {
334: f.reformatUnlock();
335: }
336:
337: sb.replace(0, sb.length(), doc.getText(0, doc.getLength()));
338: final String text = sb.toString();
339:
340: FileLock lock = targetFile.lock();
341: try {
342: BufferedWriter bw = new BufferedWriter(
343: new OutputStreamWriter(targetFile
344: .getOutputStream(lock)));
345: bw.write(text);
346: bw.close();
347: } finally {
348: lock.releaseLock();
349: }
350: } catch (FileAlreadyLockedException ex) {
351: Exceptions.printStackTrace(ex);
352: } catch (IOException ex) {
353: Exceptions.printStackTrace(ex);
354: } catch (BadLocationException ex) {
355: Exceptions.printStackTrace(ex);
356: }
357: }
358:
359: private StringBuilder generateXML(String[] incNamespaces) {
360: String sep = System.getProperty("line.separator"); // NOI18N
361: StringBuilder schemaLoc = new StringBuilder();
362: schemaLoc
363: .append(" xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"); // NOI18N
364:
365: StringBuilder sb = new StringBuilder();
366: sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>").append(
367: sep); // NOI18N
368: sb
369: .append(
370: "<beans xmlns=\"http://www.springframework.org/schema/beans\"")
371: .append(sep); // NOI18N
372: sb
373: .append(
374: " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"")
375: .append(sep); // NOI18N
376:
377: for (String cur : incNamespaces) {
378: String prefix = cur.substring(0, cur.indexOf("-")).trim(); // NOI18N
379: String schemaName = cur.substring(cur.indexOf("-") + 1)
380: .trim(); // NOI18N
381: if (!schemaName
382: .equals("http://www.springframework.org/schema/p")) { // NOI18N
383: String namespace = schemaName.substring(0, schemaName
384: .lastIndexOf("/")); // NOI18N
385: sb.append(" xmlns:").append(prefix).append("=\"")
386: .append(namespace).append("\"").append(sep); // NOI18N
387: schemaLoc.append(sep);
388: schemaLoc.append(" ").append(namespace).append(
389: " ").append(schemaName); // NOI18N
390: } else {
391: sb.append(" xmlns:").append(prefix).append("=\"")
392: .append(schemaName).append("\"").append(sep); // NOI18N
393: }
394: }
395:
396: sb.append(schemaLoc).append("\""); // NOI18N
397: sb.append(">").append(sep).append(" ").append(sep); // NOI18N
398: sb.append("</beans>"); // NOI18N
399:
400: return sb;
401: }
402:
403: static ConfigFileManager getConfigFileManager(Project p) {
404: ProjectSpringScopeProvider scopeProvider = p.getLookup()
405: .lookup(ProjectSpringScopeProvider.class);
406: ConfigFileManager manager = scopeProvider.getSpringScope()
407: .getConfigFileManager();
408: return manager;
409: }
410:
411: static FileObject getSourceGroupArtifact(Project project,
412: FileObject preferredArtifact) {
413: SourceGroup[] groups = SourceGroups
414: .getJavaSourceGroups(project);
415: for (SourceGroup group : groups) {
416: FileObject root = group.getRootFolder();
417: if (preferredArtifact.equals(root)
418: || (FileUtil.isParentOf(root, preferredArtifact) && group
419: .contains(preferredArtifact))) {
420: return preferredArtifact;
421: }
422: }
423: // Otherwise just get the first source group.
424: for (SourceGroup group : groups) {
425: return group.getRootFolder();
426: }
427: return null;
428: }
429: }
|