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.projectimport.eclipse.wizard;
043:
044: import java.awt.Component;
045: import java.awt.event.ActionEvent;
046: import java.awt.event.ActionListener;
047: import java.io.File;
048: import java.util.Collection;
049: import java.util.HashSet;
050: import java.util.Iterator;
051: import java.util.Set;
052: import java.util.Stack;
053: import java.util.TreeSet;
054: import java.util.logging.Level;
055: import java.util.logging.Logger;
056: import javax.swing.AbstractCellEditor;
057: import javax.swing.JCheckBox;
058: import javax.swing.JFileChooser;
059: import javax.swing.JPanel;
060: import javax.swing.JTable;
061: import javax.swing.ListSelectionModel;
062: import javax.swing.UIManager;
063: import javax.swing.event.DocumentEvent;
064: import javax.swing.event.DocumentListener;
065: import javax.swing.table.AbstractTableModel;
066: import javax.swing.table.TableCellEditor;
067: import javax.swing.table.TableCellRenderer;
068: import org.netbeans.modules.projectimport.LoggerFactory;
069: import org.netbeans.modules.projectimport.ProjectImporterException;
070: import org.netbeans.modules.projectimport.eclipse.EclipseProject;
071: import org.netbeans.modules.projectimport.eclipse.Workspace;
072: import org.netbeans.modules.projectimport.eclipse.WorkspaceFactory;
073: import org.openide.DialogDescriptor;
074: import org.openide.DialogDisplayer;
075: import org.openide.NotifyDescriptor;
076:
077: /**
078: * Represent "Project to import" step(panel) in the Eclipse importer wizard.
079: *
080: * @author mkrauskopf
081: */
082: final class ProjectSelectionPanel extends JPanel {
083:
084: /**
085: * Logger for this class
086: */
087: private static final Logger logger = LoggerFactory.getDefault()
088: .createLogger(ProjectSelectionPanel.class);
089:
090: /** Renderer for projects */
091: private class ProjectCellRenderer extends JCheckBox implements
092: TableCellRenderer {
093: public Component getTableCellRendererComponent(JTable table,
094: Object value, boolean isSelected, boolean hasFocus,
095: int row, int column) {
096: EclipseProject project = projects[row];
097: setColors(this , isSelected);
098: setText(project.getName());
099: setSelected(selectedProjects.contains(project)
100: || requiredProjects.contains(project));
101: setToolTipText(null);
102: if (project.hasJavaNature()
103: && !requiredProjects.contains(project)) {
104: setEnabled(true);
105: } else {
106: // required and non-java project are disabled
107: setEnabled(false);
108: if (!project.hasJavaNature()) {
109: setToolTipText(ProjectImporterWizard.getMessage(
110: "MSG_NonJavaProject", project.getName())); // NOI18N
111: }
112: }
113: return this ;
114: }
115: }
116:
117: private void setColors(Component c, boolean isSelected) {
118: c.setBackground(UIManager
119: .getColor(isSelected ? "Table.selectionBackground"
120: : "Table.background")); // NOI18N
121: c.setForeground(UIManager
122: .getColor(isSelected ? "Table.selectionForeground"
123: : "Table.foreground")); // NOI18N
124: }
125:
126: private class ProjectCellEditor extends AbstractCellEditor
127: implements TableCellEditor {
128:
129: private JCheckBox checkBox;
130:
131: public Object getCellEditorValue() {
132: return Boolean.valueOf(checkBox.isSelected());
133: }
134:
135: public Component getTableCellEditorComponent(JTable table,
136: Object value, boolean isSelected, int row, int column) {
137: EclipseProject project = projects[row];
138: checkBox = new JCheckBox(project.getName(),
139: ((Boolean) value).booleanValue());
140: setColors(checkBox, isSelected);
141: checkBox.addActionListener(new ActionListener() {
142: public void actionPerformed(ActionEvent ev) {
143: fireEditingStopped();
144: }
145: });
146: return checkBox;
147: }
148:
149: public boolean shouldSelectCell(java.util.EventObject anEvent) {
150: return true;
151: }
152: }
153:
154: /** All projects in a workspace. */
155: private EclipseProject[] projects;
156:
157: /**
158: * Projects selected by user. So it counts the projects which were selected
159: * by user and then became required (so became disabled). But project which
160: * weren't checked but are required are not members of this set.
161: * This all servers for remembering checked project when working with
162: * project dependencies.
163: */
164: private Set/*<EclipseProject>*/selectedProjects;
165:
166: /**
167: * All projects we need to import (involving projects which selected
168: * projects depend on.
169: */
170: private Set requiredProjects;
171:
172: /** Error message displayed by wizard. */
173: private String errorMessage;
174:
175: private class ProjectTableModel extends AbstractTableModel {
176: public Object getValueAt(int rowIndex, int columnIndex) {
177: EclipseProject project = projects[rowIndex];
178: return Boolean.valueOf(selectedProjects.contains(project)
179: || requiredProjects.contains(project));
180: }
181:
182: public int getRowCount() {
183: return projects != null ? projects.length : 0;
184: }
185:
186: public int getColumnCount() {
187: return 1;
188: }
189:
190: public Class getColumnClass(int columnIndex) {
191: return Boolean.class;
192: }
193:
194: public boolean isCellEditable(int rowIndex, int columnIndex) {
195: return (projects[rowIndex].hasJavaNature() && !requiredProjects
196: .contains(projects[rowIndex]));
197: }
198:
199: public void setValueAt(Object aValue, int rowIndex,
200: int columnIndex) {
201: EclipseProject project = projects[rowIndex];
202: assert projects != null;
203: if (((Boolean) aValue).booleanValue()) {
204: selectedProjects.add(project);
205: } else {
206: selectedProjects.remove(project);
207: }
208: solveDependencies();
209: fireTableDataChanged();
210: projectTable.getSelectionModel().setLeadSelectionIndex(
211: rowIndex);
212: updateValidity();
213: }
214: }
215:
216: /** Updates panel validity. */
217: public void updateValidity() {
218: if (selectedProjects == null || selectedProjects.isEmpty()) {
219: // user has to select at least one project
220: setErrorMessage(ProjectImporterWizard
221: .getMessage("MSG_ProjectIsNotChosed")); // NOI18N
222: return;
223: }
224: String parent = destination.getText();
225: for (Iterator it = allProjects().iterator(); it.hasNext();) {
226: EclipseProject prj = (EclipseProject) it.next();
227: String destDir = parent + "/" + prj.getName(); // NOI18N
228: if (new File(destDir).exists()) {
229: setErrorMessage(ProjectImporterWizard.getMessage(
230: "MSG_ProjectExist", prj.getName())); // NOI18N
231: return;
232: }
233: }
234: setErrorMessage(null);
235: }
236:
237: /** Returns both selected and required projects */
238: private Collection allProjects() {
239: Collection all = new HashSet(selectedProjects);
240: all.addAll(requiredProjects);
241: return all;
242: }
243:
244: // Helper for recursion check
245: private final Stack/*<EclipseProject>*/solved = new Stack();
246: private EclipseProject currentRoot;
247:
248: /**
249: * Solves project dependencies. Fills up <code>requiredProjects</code> as
250: * needed.
251: */
252: private void solveDependencies() {
253: requiredProjects.clear();
254: if (selectedProjects == null || selectedProjects.isEmpty()) {
255: return;
256: }
257: for (Iterator it = selectedProjects.iterator(); it.hasNext();) {
258: EclipseProject selProject = (EclipseProject) it.next();
259: assert selProject != null;
260: solved.push(selProject);
261: currentRoot = selProject;
262: fillUpRequiredProjects(selProject);
263: EclipseProject poped = (EclipseProject) solved.pop();
264: assert poped.equals(currentRoot);
265: assert solved.isEmpty();
266: currentRoot = null;
267: }
268: }
269:
270: private void fillUpRequiredProjects(EclipseProject project) {
271: Set children = project.getProjects();
272: if (children == null || children.isEmpty()) {
273: return;
274: }
275: for (Iterator it = children.iterator(); it.hasNext();) {
276: EclipseProject child = (EclipseProject) it.next();
277: assert child != null;
278: if (solved.contains(child)) {
279: recursionDetected(child);
280: return;
281: }
282: requiredProjects.add(child);
283: solved.push(child);
284: fillUpRequiredProjects(child);
285: EclipseProject poped = (EclipseProject) solved.pop();
286: assert poped.equals(child);
287: }
288: }
289:
290: private void recursionDetected(EclipseProject start) {
291: int where = solved.search(start);
292: assert where != -1 : "Cannot find start of the cycle."; // NOI18N
293: EclipseProject rootOfCycle = (EclipseProject) solved.get(solved
294: .size()
295: - where);
296: StringBuffer cycle = new StringBuffer();
297: for (Iterator it = solved.iterator(); it.hasNext();) {
298: cycle.append(((EclipseProject) it.next()).getName())
299: .append(" --> "); // NOI18N
300: }
301: cycle.append(rootOfCycle.getName()).append(" --> ..."); // NOI18N
302: logger
303: .warning("Cycle dependencies was detected. Detected cycle: "
304: + cycle); // NOI18N
305: NotifyDescriptor d = new DialogDescriptor.Message(
306: ProjectImporterWizard.getMessage(
307: "MSG_CycleDependencies", cycle.toString()), // NOI18N
308: NotifyDescriptor.WARNING_MESSAGE);
309: DialogDisplayer.getDefault().notify(d);
310: }
311:
312: /** Creates new form ProjectSelectionPanel */
313: public ProjectSelectionPanel() {
314: initComponents();
315: init();
316: destination.getDocument().addDocumentListener(
317: new DocumentListener() {
318: public void insertUpdate(DocumentEvent e) {
319: updateValidity();
320: }
321:
322: public void removeUpdate(DocumentEvent e) {
323: updateValidity();
324: }
325:
326: public void changedUpdate(DocumentEvent e) {
327: }
328: });
329: updateValidity();
330: }
331:
332: private void init() {
333: projectTable.setModel(new ProjectTableModel());
334: projectTable.setTableHeader(null);
335: projectTable.getSelectionModel().setSelectionMode(
336: ListSelectionModel.SINGLE_SELECTION);
337: projectTable.setDefaultRenderer(Boolean.class,
338: new ProjectCellRenderer());
339: projectTable.setDefaultEditor(Boolean.class,
340: new ProjectCellEditor());
341: projectTableSP.getViewport().setBackground(
342: projectTable.getBackground());
343: destination.setText(System.getProperty("user.home")); // NOI18N
344: }
345:
346: /** Loads project from workspace in the given <code>workspaceDir</code>. */
347: void loadProjects(String workspaceDir) {
348: Workspace workspace = null;
349: try {
350: workspace = WorkspaceFactory.getInstance().load(
351: workspaceDir);
352: } catch (ProjectImporterException e) {
353: setErrorMessage(ProjectImporterWizard.getMessage(
354: "MSG_WorkspaceIsInvalid", workspaceDir)); // NOI18N
355: logger.log(Level.FINE, "ProjectImporterException catched",
356: e); // NOI18N
357: return;
358: }
359: Set wsPrjs = new TreeSet(workspace.getProjects());
360: projects = new EclipseProject[wsPrjs.size()];
361: int i = 0;
362: for (Iterator it = wsPrjs.iterator(); it.hasNext();) {
363: projects[i++] = (EclipseProject) it.next();
364: }
365: selectedProjects = new HashSet();
366: requiredProjects = new HashSet();
367: if (projects.length == 0) {
368: setErrorMessage(ProjectImporterWizard.getMessage(
369: "MSG_WorkspaceIsEmpty", workspaceDir)); // NOI18N
370: } else {
371: updateValidity();
372: }
373: }
374:
375: /** Returns projects selected by selection panel */
376: Set getProjects() {
377: return selectedProjects;
378: }
379:
380: /**
381: * Returns number of projects which will be imported (including both
382: * required and selected projects)
383: */
384: int getNumberOfImportedProject() {
385: return allProjects().size();
386: }
387:
388: /**
389: * Returns destination directory where new NetBeans projects will be stored.
390: */
391: String getDestination() {
392: return destination.getText();
393: }
394:
395: void setErrorMessage(String newMessage) {
396: String oldMessage = this .errorMessage;
397: this .errorMessage = newMessage;
398: firePropertyChange("errorMessage", oldMessage, newMessage); // NOI18N
399: }
400:
401: /** This method is called from within the constructor to
402: * initialize the form.
403: * WARNING: Do NOT modify this code. The content of this method is
404: * always regenerated by the Form Editor.
405: */
406: // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
407: private void initComponents() {
408: java.awt.GridBagConstraints gridBagConstraints;
409:
410: choosePanel = new javax.swing.JPanel();
411: destination = new javax.swing.JTextField();
412: chooseDestButton = new javax.swing.JButton();
413: prjLocationLBL = new javax.swing.JLabel();
414: projectPanel = new javax.swing.JPanel();
415: projectListLabel = new javax.swing.JLabel();
416: projectTableSP = new javax.swing.JScrollPane();
417: projectTable = new javax.swing.JTable();
418:
419: setLayout(new java.awt.BorderLayout(0, 12));
420:
421: setBorder(javax.swing.BorderFactory.createEmptyBorder(5, 5, 5,
422: 5));
423: choosePanel.setLayout(new java.awt.GridBagLayout());
424:
425: gridBagConstraints = new java.awt.GridBagConstraints();
426: gridBagConstraints.gridx = 1;
427: gridBagConstraints.gridy = 0;
428: gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
429: gridBagConstraints.weightx = 1.0;
430: choosePanel.add(destination, gridBagConstraints);
431:
432: org.openide.awt.Mnemonics.setLocalizedText(chooseDestButton,
433: org.openide.util.NbBundle.getMessage(
434: ProjectSelectionPanel.class,
435: "CTL_BrowseButton_B"));
436: chooseDestButton
437: .addActionListener(new java.awt.event.ActionListener() {
438: public void actionPerformed(
439: java.awt.event.ActionEvent evt) {
440: chooseDestButtonActionPerformed(evt);
441: }
442: });
443:
444: gridBagConstraints = new java.awt.GridBagConstraints();
445: gridBagConstraints.gridx = 2;
446: gridBagConstraints.gridy = 0;
447: gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
448: gridBagConstraints.insets = new java.awt.Insets(0, 11, 0, 0);
449: choosePanel.add(chooseDestButton, gridBagConstraints);
450:
451: prjLocationLBL.setLabelFor(destination);
452: org.openide.awt.Mnemonics.setLocalizedText(prjLocationLBL,
453: org.openide.util.NbBundle.getMessage(
454: ProjectSelectionPanel.class,
455: "LBL_LocationOfNBProjects"));
456: gridBagConstraints = new java.awt.GridBagConstraints();
457: gridBagConstraints.gridx = 0;
458: gridBagConstraints.gridy = 0;
459: gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 12);
460: choosePanel.add(prjLocationLBL, gridBagConstraints);
461:
462: add(choosePanel, java.awt.BorderLayout.SOUTH);
463:
464: projectPanel.setLayout(new java.awt.GridBagLayout());
465:
466: projectListLabel.setLabelFor(projectTable);
467: org.openide.awt.Mnemonics.setLocalizedText(projectListLabel,
468: org.openide.util.NbBundle.getMessage(
469: ProjectSelectionPanel.class,
470: "LBL_ProjectsToImport"));
471: projectListLabel
472: .setVerticalTextPosition(javax.swing.SwingConstants.TOP);
473: gridBagConstraints = new java.awt.GridBagConstraints();
474: gridBagConstraints.gridx = 0;
475: gridBagConstraints.gridy = 0;
476: gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
477: gridBagConstraints.weightx = 1.0;
478: projectPanel.add(projectListLabel, gridBagConstraints);
479:
480: projectTable.setShowHorizontalLines(false);
481: projectTable.setShowVerticalLines(false);
482: projectTableSP.setViewportView(projectTable);
483:
484: gridBagConstraints = new java.awt.GridBagConstraints();
485: gridBagConstraints.gridx = 0;
486: gridBagConstraints.gridy = 1;
487: gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
488: gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
489: gridBagConstraints.weighty = 1.0;
490: gridBagConstraints.insets = new java.awt.Insets(2, 0, 0, 0);
491: projectPanel.add(projectTableSP, gridBagConstraints);
492:
493: add(projectPanel, java.awt.BorderLayout.CENTER);
494:
495: }// </editor-fold>//GEN-END:initComponents
496:
497: private void chooseDestButtonActionPerformed(
498: java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chooseDestButtonActionPerformed
499: JFileChooser chooser = new JFileChooser();
500: chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
501: int ret = chooser.showOpenDialog(this );
502: if (ret == JFileChooser.APPROVE_OPTION) {
503: destination.setText(chooser.getSelectedFile()
504: .getAbsolutePath());
505: }
506: }//GEN-LAST:event_chooseDestButtonActionPerformed
507:
508: // Variables declaration - do not modify//GEN-BEGIN:variables
509: private javax.swing.JButton chooseDestButton;
510: private javax.swing.JPanel choosePanel;
511: private javax.swing.JTextField destination;
512: private javax.swing.JLabel prjLocationLBL;
513: private javax.swing.JLabel projectListLabel;
514: private javax.swing.JPanel projectPanel;
515: private javax.swing.JTable projectTable;
516: private javax.swing.JScrollPane projectTableSP;
517: // End of variables declaration//GEN-END:variables
518: }
|