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:
042: package org.netbeans.modules.tomcat5.customizer;
043:
044: import java.awt.Component;
045: import java.awt.GridBagConstraints;
046: import java.awt.GridBagLayout;
047: import java.awt.Insets;
048: import java.awt.event.ActionEvent;
049: import java.awt.event.ActionListener;
050: import java.io.File;
051: import java.net.MalformedURLException;
052: import java.net.URI;
053: import java.net.URL;
054: import java.util.ArrayList;
055: import java.util.Arrays;
056: import java.util.Collection;
057: import java.util.Iterator;
058: import java.util.List;
059: import java.util.StringTokenizer;
060: import java.util.logging.Level;
061: import java.util.logging.Logger;
062: import javax.swing.AbstractListModel;
063: import javax.swing.JButton;
064: import javax.swing.JFileChooser;
065: import javax.swing.JLabel;
066: import javax.swing.JList;
067: import javax.swing.JPanel;
068: import javax.swing.JScrollPane;
069: import javax.swing.event.ListSelectionEvent;
070: import javax.swing.event.ListSelectionListener;
071: import javax.swing.filechooser.FileFilter;
072: import org.netbeans.modules.j2ee.deployment.common.api.J2eeLibraryTypeProvider;
073: import org.openide.NotifyDescriptor;
074: import org.openide.filesystems.FileUtil;
075: import org.openide.util.Exceptions;
076: import org.openide.util.NbBundle;
077:
078: /**
079: * Server customizer support class. Provides default implementations of some
080: * common server manager customizer panes.
081: *
082: * @author sherold
083: *
084: * @since 1.19
085: */
086: public final class CustomizerSupport {
087:
088: private static final String CLASSPATH = J2eeLibraryTypeProvider.VOLUME_TYPE_CLASSPATH;
089: private static final String SOURCES = J2eeLibraryTypeProvider.VOLUME_TYPE_SRC;
090: private static final String JAVADOC = J2eeLibraryTypeProvider.VOLUME_TYPE_JAVADOC;
091:
092: /** Do not allow to create instances of this class */
093: private CustomizerSupport() {
094: }
095:
096: /**
097: * Creates non-editable customizer classes pane.
098: *
099: * @param model A model prepresenting the class path entries.
100: *
101: * @return A Component representing the classes pane.
102: *
103: * @throws NullPointerException If null model is passed in.
104: */
105: public static Component createClassesCustomizer(PathModel model) {
106: if (model == null) {
107: throw new NullPointerException();
108: }
109: return new PathView(model, CLASSPATH, null);
110: }
111:
112: /**
113: * Creates an editable customizer sources pane.
114: *
115: * @param model A model prepresenting the source path entries.
116: * @param currentDir Add sources file chooser current directory. Passing in
117: * a null represents the user's default directory.
118: *
119: * @return A Component representing the sources pane.
120: *
121: * @throws NullPointerException If null model is passed in.
122: */
123: public static Component createSourcesCustomizer(PathModel model,
124: File currentDir) {
125: if (model == null) {
126: throw new NullPointerException();
127: }
128: return new PathView(model, SOURCES, currentDir);
129: }
130:
131: /**
132: * Creates an editable customizer javadoc pane.
133: *
134: * @param model A model prepresenting the javadoc entries.
135: * @param currentDir Add javadoc file chooser current directory. Passing in
136: * a null represents the user's default directory.
137: *
138: * @return A Component representing the javadoc pane.
139: *
140: * @throws NullPointerException If null model is passed in.
141: */
142: public static Component createJavadocCustomizer(PathModel model,
143: File currentDir) {
144: if (model == null) {
145: throw new NullPointerException();
146: }
147: return new PathView(model, JAVADOC, currentDir);
148: }
149:
150: /**
151: * Creates an Ant-style path specification from the specified list of URLs.
152: *
153: * @param The list of URLs.
154: *
155: * @return An Ant-style path specification.
156: */
157: public static String buildPath(List<URL> path) {
158: String PATH_SEPARATOR = System.getProperty("path.separator"); // NOI18N
159: StringBuffer sb = new StringBuffer(path.size() * 16);
160: for (Iterator<URL> i = path.iterator(); i.hasNext();) {
161: sb.append(urlToString(i.next()));
162: if (i.hasNext()) {
163: sb.append(PATH_SEPARATOR);
164: }
165: }
166: return sb.toString();
167: }
168:
169: /**
170: * Splits an Ant-style path specification into the list of URLs. Tokenizes on
171: * <code>:</code> and <code>;</code>, paying attention to DOS-style components
172: * such as <samp>C:\FOO</samp>. Also removes any empty components.
173: *
174: * @param path An Ant-style path (elements arbitrary) using DOS or Unix separators
175: *
176: * @return A tokenization of the specified path into the list of URLs.
177: */
178: public static List<URL> tokenizePath(String path) {
179: try {
180: List<URL> l = new ArrayList();
181: StringTokenizer tok = new StringTokenizer(path, ":;", true); // NOI18N
182: char dosHack = '\0';
183: char lastDelim = '\0';
184: int delimCount = 0;
185: while (tok.hasMoreTokens()) {
186: String s = tok.nextToken();
187: if (s.length() == 0) {
188: // Strip empty components.
189: continue;
190: }
191: if (s.length() == 1) {
192: char c = s.charAt(0);
193: if (c == ':' || c == ';') {
194: // Just a delimiter.
195: lastDelim = c;
196: delimCount++;
197: continue;
198: }
199: }
200: if (dosHack != '\0') {
201: // #50679 - "C:/something" is also accepted as DOS path
202: if (lastDelim == ':'
203: && delimCount == 1
204: && (s.charAt(0) == '\\' || s.charAt(0) == '/')) {
205: // We had a single letter followed by ':' now followed by \something or /something
206: s = "" + dosHack + ':' + s;
207: // and use the new token with the drive prefix...
208: } else {
209: // Something else, leave alone.
210: l.add(fileToUrl(new File(Character
211: .toString(dosHack))));
212: // and continue with this token too...
213: }
214: dosHack = '\0';
215: }
216: // Reset count of # of delimiters in a row.
217: delimCount = 0;
218: if (s.length() == 1) {
219: char c = s.charAt(0);
220: if ((c >= 'a' && c <= 'z')
221: || (c >= 'A' && c <= 'Z')) {
222: // Probably a DOS drive letter. Leave it with the next component.
223: dosHack = c;
224: continue;
225: }
226: }
227: l.add(fileToUrl(new File(s)));
228: }
229: if (dosHack != '\0') {
230: //the dosHack was the last letter in the input string (not followed by the ':')
231: //so obviously not a drive letter.
232: //Fix for issue #57304
233: l.add(fileToUrl(new File(Character.toString(dosHack))));
234: }
235: return l;
236: } catch (MalformedURLException e) {
237: Exceptions.printStackTrace(e);
238: return new ArrayList();
239: }
240: }
241:
242: /** Return URL representation of the specified file. */
243: private static URL fileToUrl(File file)
244: throws MalformedURLException {
245: URL url = file.toURI().toURL();
246: if (FileUtil.isArchiveFile(url)) {
247: url = FileUtil.getArchiveRoot(url);
248: }
249: return url;
250: }
251:
252: /** Return string representation of the specified URL. */
253: private static String urlToString(URL url) {
254: if ("jar".equals(url.getProtocol())) { // NOI18N
255: URL fileURL = FileUtil.getArchiveFile(url);
256: if (FileUtil.getArchiveRoot(fileURL).equals(url)) {
257: // really the root
258: url = fileURL;
259: } else {
260: // some subdir, just show it as is
261: return url.toExternalForm();
262: }
263: }
264: if ("file".equals(url.getProtocol())) { // NOI18N
265: File f = new File(URI.create(url.toExternalForm()));
266: return f.getAbsolutePath();
267: } else {
268: return url.toExternalForm();
269: }
270: }
271:
272: /**
273: * Path list model, supports adding, removing and moving URL entries in the list.
274: */
275: public static final class PathModel extends AbstractListModel {
276:
277: private final List<URL> data;
278:
279: /**
280: * Creates a new PathModel initialized with a list of URL entries.
281: *
282: * @param data The list of URL entries.
283: *
284: * @throws NullPointerException If null data attribute is passed in.
285: */
286: public PathModel(List<URL> data) {
287: if (data == null) {
288: throw new NullPointerException(
289: "The data attribute must not be null."); // NOI18N
290: }
291: this .data = data;
292: }
293:
294: /**
295: * Returns the number of URL entries in the list.
296: *
297: * return The number of URL entries in the list.
298: */
299: public int getSize() {
300: return data.size();
301: }
302:
303: /**
304: * Returns the element at the specified position in this list.
305: *
306: * @param index The element position in the list.
307: *
308: * @return The element at the specified position in this list.
309: */
310: public Object getElementAt(int index) {
311: URL url = data.get(index);
312: if ("jar".equals(url.getProtocol())) { // NOI18N
313: URL fileURL = FileUtil.getArchiveFile(url);
314: if (FileUtil.getArchiveRoot(fileURL).equals(url)) {
315: // really the root
316: url = fileURL;
317: } else {
318: // some subdir, just show it as is
319: return url.toExternalForm();
320: }
321: }
322: if ("file".equals(url.getProtocol())) { // NOI18N
323: File f = new File(URI.create(url.toExternalForm()));
324: return f.getAbsolutePath();
325: } else {
326: return url.toExternalForm();
327: }
328: }
329:
330: /**
331: * Removes the URL entries denotated with their respective indices from the list.
332: */
333: public void removePath(int[] indices) {
334: for (int i = indices.length - 1; i >= 0; i--) {
335: data.remove(indices[i]);
336: }
337: fireIntervalRemoved(this , indices[0],
338: indices[indices.length - 1]);
339: }
340:
341: /**
342: * Moves the URL entries denotated with their respective indices up in the list.
343: */
344: public void moveUpPath(int[] indices) {
345: for (int i = 0; i < indices.length; i++) {
346: URL p2 = data.get(indices[i]);
347: URL p1 = data.set(indices[i] - 1, p2);
348: data.set(indices[i], p1);
349: }
350: fireContentsChanged(this , indices[0] - 1,
351: indices[indices.length - 1]);
352: }
353:
354: /**
355: * Moves the URL entries denotated with their respective indices down in the list.
356: */
357: public void moveDownPath(int[] indices) {
358: for (int i = indices.length - 1; i >= 0; i--) {
359: URL p1 = data.get(indices[i]);
360: URL p2 = data.set(indices[i] + 1, p1);
361: data.set(indices[i], p2);
362: }
363: fireContentsChanged(this , indices[0],
364: indices[indices.length - 1] + 1);
365: }
366:
367: /**
368: * Appends the URL representing the specified file to the end of the list.
369: *
370: * @return true if the URL was appended, false otherwise.
371: */
372: public boolean addPath(File f) {
373: try {
374: URL url = f.toURI().toURL();
375: return this .addPath(url);
376: } catch (MalformedURLException mue) {
377: return false;
378: }
379: }
380:
381: /**
382: * Appends the specified URL to the end of the list.
383: *
384: * @return true if the URL was appended, false otherwise.
385: */
386: public boolean addPath(URL url) {
387: if (FileUtil.isArchiveFile(url)) {
388: url = FileUtil.getArchiveRoot(url);
389: } else if (!url.toExternalForm().endsWith("/")) { // NOI18N
390: try {
391: url = new URL(url.toExternalForm() + "/"); // NOI18N
392: } catch (MalformedURLException mue) {
393: Logger.getLogger(CustomizerSupport.class.getName())
394: .log(Level.INFO, null, mue);
395: }
396: }
397: int oldSize = data.size();
398: data.add(url);
399: fireIntervalAdded(this , oldSize, oldSize);
400: return true;
401: }
402:
403: /**
404: * Returns the list of URL entries.
405: * @return The list of URL entries.
406: */
407: public List<URL> getData() {
408: return data;
409: }
410: }
411:
412: // private helper classes -------------------------------------------------
413:
414: private static class PathView extends JPanel {
415:
416: private JList resources;
417: private JButton addButton;
418: private JButton addURLButton;
419: private JButton removeButton;
420: private JButton moveUpButton;
421: private JButton moveDownButton;
422: private File currentDir;
423: private String type;
424:
425: public PathView(PathModel model, String type, File currentDir) {
426: this .type = type;
427: this .currentDir = currentDir;
428: initComponents(model);
429: }
430:
431: private void initComponents(PathModel model) {
432: setLayout(new GridBagLayout());
433: JLabel label = new JLabel();
434: String key = null;
435: String ad = null;
436: if (type.equals(CLASSPATH)) {
437: key = "TXT_Classes"; // NOI18N
438: ad = "AD_Classes"; // NOI18N
439: } else if (type.equals(SOURCES)) {
440: key = "TXT_Sources"; // NOI18N
441: ad = "AD_Sources"; // NOI18N
442: } else if (type.equals(JAVADOC)) {
443: key = "TXT_Javadoc"; // NOI18N
444: ad = "AD_Javadoc"; // NOI18N
445: } else {
446: assert false : "Illegal type of panel"; //NOI18N
447: return;
448: }
449: org.openide.awt.Mnemonics.setLocalizedText(label, NbBundle
450: .getMessage(CustomizerSupport.class, key));
451: GridBagConstraints c = new GridBagConstraints();
452: c.gridx = GridBagConstraints.RELATIVE;
453: c.gridy = GridBagConstraints.RELATIVE;
454: c.gridwidth = GridBagConstraints.REMAINDER;
455: c.insets = new Insets(6, 12, 2, 0);
456: c.fill = GridBagConstraints.HORIZONTAL;
457: c.weightx = 1.0;
458: ((GridBagLayout) getLayout()).setConstraints(label, c);
459: add(label);
460: resources = new JList(model);
461: label.setLabelFor(resources);
462: resources.getAccessibleContext().setAccessibleDescription(
463: NbBundle.getMessage(CustomizerSupport.class, ad));
464: resources
465: .addListSelectionListener(new ListSelectionListener() {
466: public void valueChanged(ListSelectionEvent e) {
467: selectionChanged();
468: }
469: });
470: JScrollPane spane = new JScrollPane(this .resources);
471: // set the preferred size so that the size won't be set according to
472: // the longest row in the list by default
473: spane.setPreferredSize(new java.awt.Dimension(200, 100));
474: c = new GridBagConstraints();
475: c.gridx = GridBagConstraints.RELATIVE;
476: c.gridy = GridBagConstraints.RELATIVE;
477: c.gridwidth = 1;
478: c.gridheight = 5;
479: c.insets = new Insets(0, 12, 12, 6);
480: c.fill = GridBagConstraints.BOTH;
481: c.weightx = 1.0;
482: c.weighty = 1.0;
483: ((GridBagLayout) this .getLayout()).setConstraints(spane, c);
484: add(spane);
485: if (type == SOURCES || type == JAVADOC) {
486: this .addButton = new JButton();
487: String text;
488: if (type == SOURCES) {
489: text = NbBundle.getMessage(CustomizerSupport.class,
490: "CTL_Add");
491: ad = NbBundle.getMessage(CustomizerSupport.class,
492: "AD_Add");
493: } else {
494: text = NbBundle.getMessage(CustomizerSupport.class,
495: "CTL_AddZip");
496: ad = NbBundle.getMessage(CustomizerSupport.class,
497: "AD_AddZip");
498: }
499: org.openide.awt.Mnemonics.setLocalizedText(addButton,
500: text);
501: this .addButton.getAccessibleContext()
502: .setAccessibleDescription(ad);
503: addButton.addActionListener(new ActionListener() {
504: public void actionPerformed(ActionEvent e) {
505: addPathElement();
506: }
507: });
508: c = new GridBagConstraints();
509: c.gridx = 1;
510: c.gridy = 1;
511: c.gridwidth = GridBagConstraints.REMAINDER;
512: c.fill = GridBagConstraints.HORIZONTAL;
513: c.anchor = GridBagConstraints.NORTHWEST;
514: c.insets = new Insets(0, 6, 0, 6);
515: ((GridBagLayout) this .getLayout()).setConstraints(
516: addButton, c);
517: this .add(addButton);
518: // if (this.type == JAVADOC) {
519: // addURLButton = new JButton();
520: // org.openide.awt.Mnemonics.setLocalizedText(addURLButton, NbBundle.getMessage(CustomizerSupport.class, "CTL_AddURL")); // NOI18N
521: // addURLButton.addActionListener(new ActionListener () {
522: // public void actionPerformed(ActionEvent e) {
523: // addURLElement ();
524: // }
525: // });
526: // c = new GridBagConstraints();
527: // c.gridx = 1;
528: // c.gridy = 2;
529: // c.gridwidth = GridBagConstraints.REMAINDER;
530: // c.fill = GridBagConstraints.HORIZONTAL;
531: // c.anchor = GridBagConstraints.NORTHWEST;
532: // c.insets = new Insets (0,6,6,12);
533: // ((GridBagLayout)this.getLayout()).setConstraints(addURLButton,c);
534: // this.add (addURLButton);
535: // }
536: removeButton = new JButton();
537: org.openide.awt.Mnemonics.setLocalizedText(
538: removeButton, NbBundle.getMessage(
539: CustomizerStartup.class, "CTL_Remove")); // NOI18N
540: removeButton.getAccessibleContext()
541: .setAccessibleDescription(
542: NbBundle.getMessage(
543: CustomizerSupport.class,
544: "AD_Remove"));
545: removeButton.addActionListener(new ActionListener() {
546: public void actionPerformed(ActionEvent e) {
547: removePathElement();
548: }
549: });
550: removeButton.setEnabled(false);
551: c = new GridBagConstraints();
552: c.gridx = 1;
553: c.gridy = 3;
554: c.gridwidth = GridBagConstraints.REMAINDER;
555: c.fill = GridBagConstraints.HORIZONTAL;
556: c.anchor = GridBagConstraints.NORTHWEST;
557: c.insets = new Insets(12, 6, 0, 6);
558: ((GridBagLayout) this .getLayout()).setConstraints(
559: removeButton, c);
560: this .add(removeButton);
561: moveUpButton = new JButton();
562: org.openide.awt.Mnemonics.setLocalizedText(
563: moveUpButton, NbBundle.getMessage(
564: CustomizerSupport.class, "CTL_Up")); // NOI18N
565: moveUpButton.getAccessibleContext()
566: .setAccessibleDescription(
567: NbBundle.getMessage(
568: CustomizerSupport.class,
569: "AD_Up"));
570: moveUpButton.addActionListener(new ActionListener() {
571: public void actionPerformed(ActionEvent e) {
572: moveUpPathElement();
573: }
574: });
575: moveUpButton.setEnabled(false);
576: c = new GridBagConstraints();
577: c.gridx = 1;
578: c.gridy = 4;
579: c.gridwidth = GridBagConstraints.REMAINDER;
580: c.fill = GridBagConstraints.HORIZONTAL;
581: c.anchor = GridBagConstraints.NORTHWEST;
582: c.insets = new Insets(12, 6, 0, 6);
583: ((GridBagLayout) this .getLayout()).setConstraints(
584: moveUpButton, c);
585: this .add(moveUpButton);
586: moveDownButton = new JButton();
587: org.openide.awt.Mnemonics.setLocalizedText(
588: moveDownButton, NbBundle.getMessage(
589: CustomizerSupport.class, "CTL_Down")); // NOI18N
590: moveDownButton.getAccessibleContext()
591: .setAccessibleDescription(
592: NbBundle.getMessage(
593: CustomizerSupport.class,
594: "AD_Down"));
595: moveDownButton.addActionListener(new ActionListener() {
596: public void actionPerformed(ActionEvent e) {
597: moveDownPathElement();
598: }
599: });
600: moveDownButton.setEnabled(false);
601: c = new GridBagConstraints();
602: c.gridx = 1;
603: c.gridy = 5;
604: c.gridwidth = GridBagConstraints.REMAINDER;
605: c.fill = GridBagConstraints.HORIZONTAL;
606: c.anchor = GridBagConstraints.NORTHWEST;
607: c.insets = new Insets(5, 6, 6, 6);
608: ((GridBagLayout) this .getLayout()).setConstraints(
609: moveDownButton, c);
610: this .add(moveDownButton);
611: }
612: }
613:
614: // private void addURLElement() {
615: // JPanel p = new JPanel ();
616: // GridBagLayout lm = new GridBagLayout();
617: // p.setLayout (lm);
618: // GridBagConstraints c = new GridBagConstraints ();
619: // c.gridx = c.gridy = GridBagConstraints.RELATIVE;
620: // c.insets = new Insets (12,12,12,6);
621: // c.anchor = GridBagConstraints.NORTHWEST;
622: // JLabel label = new JLabel (NbBundle.getMessage(CustomizerSupport.class,"CTL_AddJavadocURLMessage"));
623: // label.setDisplayedMnemonic ('U');
624: // lm.setConstraints(label,c);
625: // p.add (label);
626: // c = new GridBagConstraints ();
627: // c.gridx = c.gridy = GridBagConstraints.RELATIVE;
628: // c.gridwidth = GridBagConstraints.REMAINDER;
629: // c.insets = new Insets (12,0,12,6);
630: // c.fill = GridBagConstraints.HORIZONTAL;
631: // c.anchor = GridBagConstraints.NORTHWEST;
632: // JTextField text = new JTextField ();
633: // text.setColumns(30);
634: // text.setText (NbBundle.getMessage(CustomizerSupport.class,"TXT_DefaultProtocol"));
635: // text.selectAll();
636: // label.setLabelFor(text);
637: // lm.setConstraints(text,c);
638: // p.add (text);
639: // JButton[] options = new JButton[] {
640: // new JButton(),
641: // new JButton()
642: // };
643: // org.openide.awt.Mnemonics.setLocalizedText(options[0], NbBundle.getMessage(CustomizerSupport.class,"CTL_AddJavadocURLTitle")); // NOI18N
644: // org.openide.awt.Mnemonics.setLocalizedText(options[1], NbBundle.getMessage(CustomizerSupport.class,"CTL_Cancel")); // NOI18N
645: // DialogDescriptor input = new DialogDescriptor (
646: // p,
647: // NbBundle.getMessage(CustomizerSupport.class,"CTL_AddJavadocURLTitle"),
648: // true, options, options[0], DialogDescriptor.DEFAULT_ALIGN, null, null);
649: // if (DialogDisplayer.getDefault().notify(input) == options[0]) {
650: // try {
651: // String value = text.getText();
652: // URL url = new URL (value);
653: // ((PathModel)this.resources.getModel()).addPath(url);
654: // this.resources.setSelectedIndex (this.resources.getModel().getSize()-1);
655: // } catch (MalformedURLException mue) {
656: // DialogDescriptor.Message message = new DialogDescriptor.Message (
657: // NbBundle.getMessage(CustomizerSupport.class,"CTL_InvalidURLFormat"),
658: // DialogDescriptor.ERROR_MESSAGE);
659: // DialogDisplayer.getDefault().notify(message);
660: // }
661: // }
662: // }
663:
664: private void addPathElement() {
665: JFileChooser chooser = new JFileChooser();
666: FileUtil.preventFileChooserSymlinkTraversal(chooser, null);
667: chooser.setMultiSelectionEnabled(true);
668: String title = null;
669: String message = null;
670: String approveButtonName = null;
671: String approveButtonNameMne = null;
672: if (SOURCES.equals(this .type)) {
673: title = NbBundle.getMessage(CustomizerSupport.class,
674: "TXT_OpenSources");
675: message = NbBundle.getMessage(CustomizerSupport.class,
676: "TXT_FilterSources");
677: approveButtonName = NbBundle.getMessage(
678: CustomizerSupport.class, "TXT_OpenSources");
679: approveButtonNameMne = NbBundle.getMessage(
680: CustomizerSupport.class, "MNE_OpenSources");
681: } else if (JAVADOC.equals(this .type)) {
682: title = NbBundle.getMessage(CustomizerSupport.class,
683: "TXT_OpenJavadoc");
684: message = NbBundle.getMessage(CustomizerSupport.class,
685: "TXT_FilterJavadoc");
686: approveButtonName = NbBundle.getMessage(
687: CustomizerSupport.class, "TXT_OpenJavadoc");
688: approveButtonNameMne = NbBundle.getMessage(
689: CustomizerSupport.class, "MNE_OpenJavadoc");
690: } else {
691: throw new IllegalStateException(
692: "Can't add element for classpath"); // NOI18N
693: }
694: chooser.setDialogTitle(title);
695: chooser.setApproveButtonText(approveButtonName);
696: chooser.setApproveButtonMnemonic(approveButtonNameMne
697: .charAt(0));
698: chooser
699: .setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
700: //#61789 on old macosx (jdk 1.4.1) these two method need to be called in this order.
701: chooser.setAcceptAllFileFilterUsed(false);
702: chooser.setFileFilter(new SimpleFileFilter(message,
703: new String[] { "ZIP", "JAR" })); //NOI18N
704: if (this .currentDir != null && currentDir.exists()) {
705: chooser.setCurrentDirectory(this .currentDir);
706: }
707: if (chooser.showOpenDialog(this ) == JFileChooser.APPROVE_OPTION) {
708: File[] fs = chooser.getSelectedFiles();
709: PathModel model = (PathModel) this .resources.getModel();
710: boolean addingFailed = false;
711: int firstIndex = this .resources.getModel().getSize();
712: for (int i = 0; i < fs.length; i++) {
713: File f = fs[i];
714: //XXX: JFileChooser workaround (JDK bug #5075580), double click on folder returns wrong file
715: // E.g. for /foo/src it returns /foo/src/src
716: // Try to convert it back by removing last invalid name component
717: if (!f.exists()) {
718: File parent = f.getParentFile();
719: if (parent != null
720: && f.getName().equals(parent.getName())
721: && parent.exists()) {
722: f = parent;
723: }
724: }
725: addingFailed |= !model.addPath(f);
726: }
727: if (addingFailed) {
728: new NotifyDescriptor.Message(NbBundle.getMessage(
729: CustomizerSupport.class,
730: "TXT_CanNotAddResolve"),
731: NotifyDescriptor.ERROR_MESSAGE);
732: }
733: int lastIndex = this .resources.getModel().getSize() - 1;
734: if (firstIndex <= lastIndex) {
735: int[] toSelect = new int[lastIndex - firstIndex + 1];
736: for (int i = 0; i < toSelect.length; i++) {
737: toSelect[i] = firstIndex + i;
738: }
739: this .resources.setSelectedIndices(toSelect);
740: }
741: this .currentDir = FileUtil.normalizeFile(chooser
742: .getCurrentDirectory());
743: }
744: }
745:
746: private void removePathElement() {
747: int[] indices = this .resources.getSelectedIndices();
748: if (indices.length == 0) {
749: return;
750: }
751: PathModel model = (PathModel) this .resources.getModel();
752: model.removePath(indices);
753: if (indices[indices.length - 1] - indices.length + 1 < this .resources
754: .getModel().getSize()) {
755: this .resources
756: .setSelectedIndex(indices[indices.length - 1]
757: - indices.length + 1);
758: } else if (indices[0] > 0) {
759: this .resources.setSelectedIndex(indices[0] - 1);
760: }
761: }
762:
763: private void moveDownPathElement() {
764: int[] indices = this .resources.getSelectedIndices();
765: if (indices.length == 0) {
766: return;
767: }
768: PathModel model = (PathModel) this .resources.getModel();
769: model.moveDownPath(indices);
770: for (int i = 0; i < indices.length; i++) {
771: indices[i] = indices[i] + 1;
772: }
773: this .resources.setSelectedIndices(indices);
774: }
775:
776: private void moveUpPathElement() {
777: int[] indices = this .resources.getSelectedIndices();
778: if (indices.length == 0) {
779: return;
780: }
781: PathModel model = (PathModel) this .resources.getModel();
782: model.moveUpPath(indices);
783: for (int i = 0; i < indices.length; i++) {
784: indices[i] = indices[i] - 1;
785: }
786: this .resources.setSelectedIndices(indices);
787: }
788:
789: private void selectionChanged() {
790: if (this .type == CLASSPATH) {
791: return;
792: }
793: int indices[] = this .resources.getSelectedIndices();
794: this .removeButton.setEnabled(indices.length > 0);
795: this .moveUpButton.setEnabled(indices.length > 0
796: && indices[0] > 0);
797: this .moveDownButton.setEnabled(indices.length > 0
798: && indices[indices.length - 1] < this .resources
799: .getModel().getSize() - 1);
800: }
801: }
802:
803: private static class SimpleFileFilter extends FileFilter {
804:
805: private String description;
806: private Collection extensions;
807:
808: public SimpleFileFilter(String description, String[] extensions) {
809: this .description = description;
810: this .extensions = Arrays.asList(extensions);
811: }
812:
813: public boolean accept(File f) {
814: if (f.isDirectory())
815: return true;
816: String name = f.getName();
817: int index = name.lastIndexOf('.'); //NOI18N
818: if (index <= 0 || index == name.length() - 1)
819: return false;
820: String extension = name.substring(index + 1).toUpperCase();
821: return this .extensions.contains(extension);
822: }
823:
824: public String getDescription() {
825: return this.description;
826: }
827: }
828: }
|