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: package org.netbeans.modules.refactoring.java.ui;
042:
043: import com.sun.source.tree.ClassTree;
044: import com.sun.source.tree.Tree;
045: import com.sun.source.util.TreePath;
046: import java.awt.Component;
047: import java.awt.Dimension;
048: import java.io.IOException;
049: import java.util.ArrayList;
050: import java.util.Collections;
051: import java.util.Comparator;
052: import java.util.List;
053: import java.util.Set;
054: import javax.lang.model.element.Element;
055: import javax.lang.model.element.ElementKind;
056: import javax.lang.model.element.Modifier;
057: import javax.lang.model.type.TypeMirror;
058: import javax.swing.JPanel;
059: import javax.swing.JTable;
060: import javax.swing.UIManager;
061: import javax.swing.event.ChangeListener;
062: import javax.swing.event.DocumentEvent;
063: import javax.swing.event.DocumentListener;
064: import javax.swing.table.AbstractTableModel;
065: import javax.swing.table.DefaultTableCellRenderer;
066: import org.netbeans.api.java.source.CancellableTask;
067: import org.netbeans.api.java.source.CompilationController;
068: import org.netbeans.api.java.source.JavaSource;
069: import org.netbeans.api.java.source.TreePathHandle;
070: import org.netbeans.api.java.source.UiUtils.PrintPart;
071: import org.netbeans.modules.refactoring.java.api.ExtractSuperclassRefactoring;
072: import org.netbeans.modules.refactoring.java.api.MemberInfo;
073: import org.netbeans.modules.refactoring.spi.ui.CustomRefactoringPanel;
074: import org.openide.filesystems.FileObject;
075: import org.openide.util.NbBundle;
076:
077: /** UI panel for collecting refactoring parameters.
078: *
079: * @author Martin Matula, Jan Pokorsky
080: */
081: public class ExtractSuperclassPanel extends JPanel implements
082: CustomRefactoringPanel {
083: // helper constants describing columns in the table of members
084: private static final String[] COLUMN_NAMES = { "LBL_Selected",
085: "LBL_ExtractSC_Member", "LBL_ExtractSC_MakeAbstract" }; // NOI18N
086: private static final Class[] COLUMN_CLASSES = { Boolean.class,
087: MemberInfo.class, Boolean.class };
088:
089: // refactoring this panel provides parameters for
090: private final ExtractSuperclassRefactoring refactoring;
091: // table model for the table of members
092: private final TableModel tableModel;
093: // data for the members table (first dimension - rows, second dimension - columns)
094: // the columns are: 0 = Selected (true/false), 1 = Member (Java element), 2 = Make Abstract (true/false)
095: private Object[][] members = new Object[0][0];
096:
097: /** Creates new form ExtractSuperclassPanel
098: * @param refactoring The refactoring this panel provides parameters for.
099: * @param selectedMembers Members that should be pre-selected in the panel
100: * (determined by which nodes the action was invoked on - e.g. if it was
101: * invoked on a method, the method will be pre-selected to be pulled up)
102: */
103: public ExtractSuperclassPanel(
104: ExtractSuperclassRefactoring refactoring,
105: final ChangeListener parent) {
106: this .refactoring = refactoring;
107: this .tableModel = new TableModel();
108: initComponents();
109: setPreferredSize(new Dimension(420, 380));
110: String defaultName = "NewClass"; //NOI18N
111: nameText.setText(defaultName);
112: nameText.setSelectionStart(0);
113: nameText.setSelectionEnd(defaultName.length());
114:
115: nameText.getDocument().addDocumentListener(
116: new DocumentListener() {
117: public void changedUpdate(DocumentEvent event) {
118: parent.stateChanged(null);
119: }
120:
121: public void insertUpdate(DocumentEvent event) {
122: parent.stateChanged(null);
123: }
124:
125: public void removeUpdate(DocumentEvent event) {
126: parent.stateChanged(null);
127: }
128: });
129: }
130:
131: public void requestFocus() {
132: super .requestFocus();
133: nameText.requestFocus();
134: }
135:
136: /** Initialization of the panel (called by the parent window).
137: */
138: public void initialize() {
139: // *** initialize table
140: // set renderer for the second column ("Member") do display name of the feature
141: membersTable.setDefaultRenderer(COLUMN_CLASSES[1],
142: new DefaultTableCellRenderer() {
143: public Component getTableCellRendererComponent(
144: JTable table, Object value,
145: boolean isSelected, boolean hasFocus,
146: int row, int column) {
147: super .getTableCellRendererComponent(table,
148: extractText(value), isSelected,
149: hasFocus, row, column);
150: if (value instanceof MemberInfo) {
151: setIcon(((MemberInfo) value).getIcon());
152: }
153: return this ;
154: }
155:
156: protected String extractText(Object value) {
157: String displayValue;
158: if (value instanceof MemberInfo) {
159: displayValue = ((MemberInfo) value)
160: .getHtmlText();
161: } else {
162: displayValue = String.valueOf(value);
163: }
164: return displayValue;
165: }
166: });
167: // send renderer for the third column ("Make Abstract") to make the checkbox:
168: // 1. hidden for elements that are not methods
169: // 2. be disabled for static methods
170: membersTable.getColumnModel().getColumn(2).setCellRenderer(
171: new UIUtilities.BooleanTableCellRenderer());
172: // set background color of the scroll pane to be the same as the background
173: // of the table
174: scrollPane.setBackground(membersTable.getBackground());
175: scrollPane.getViewport().setBackground(
176: membersTable.getBackground());
177: // set default row height
178: membersTable.setRowHeight(18);
179: // set grid color to be consistent with other netbeans tables
180: if (UIManager.getColor("control") != null) { // NOI18N
181: membersTable.setGridColor(UIManager.getColor("control")); // NOI18N
182: }
183: // compute and set the preferred width for the first and the third column
184: UIUtilities.initColumnWidth(membersTable, 0, Boolean.TRUE, 4);
185: UIUtilities.initColumnWidth(membersTable, 2, Boolean.TRUE, 4);
186: }
187:
188: // --- GETTERS FOR REFACTORING PARAMETERS ----------------------------------
189:
190: public String getSuperClassName() {
191: return nameText.getText();
192: }
193:
194: /** Getter used by the refactoring UI to get members to be pulled up.
195: * @return Descriptors of members to be pulled up.
196: */
197: public MemberInfo[] getMembers() {
198: List<MemberInfo> list = new ArrayList<MemberInfo>();
199: // go through all rows of a table and collect selected members
200: for (int i = 0; i < members.length; i++) {
201: // if the current row is selected, create MemberInfo for it and
202: // add it to the list of selected members
203: if (members[i][0].equals(Boolean.TRUE)) {
204: MemberInfo member = (MemberInfo) members[i][1];
205: member.setMakeAbstract(members[i][2] != null
206: && ((Boolean) members[i][2]));
207: list.add(member);
208: }
209: }
210: // return the array of selected members
211: return (MemberInfo[]) list.toArray(new MemberInfo[list.size()]);
212: }
213:
214: // --- GENERATED CODE ------------------------------------------------------
215:
216: /** This method is called from within the constructor to
217: * initialize the form.
218: * WARNING: Do NOT modify this code. The content of this method is
219: * always regenerated by the Form Editor.
220: */
221: // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
222: private void initComponents() {
223:
224: namePanel = new javax.swing.JPanel();
225: nameLabel = new javax.swing.JLabel();
226: chooseLabel = new javax.swing.JLabel();
227: nameText = new javax.swing.JTextField();
228: scrollPane = new javax.swing.JScrollPane();
229: membersTable = new javax.swing.JTable();
230:
231: setBorder(javax.swing.BorderFactory.createEmptyBorder(12, 12,
232: 11, 11));
233: setLayout(new java.awt.BorderLayout());
234:
235: namePanel.setBorder(javax.swing.BorderFactory
236: .createEmptyBorder(1, 1, 1, 1));
237: namePanel.setLayout(new java.awt.BorderLayout(12, 0));
238:
239: nameLabel.setLabelFor(nameText);
240: org.openide.awt.Mnemonics.setLocalizedText(nameLabel,
241: org.openide.util.NbBundle.getMessage(
242: ExtractSuperclassPanel.class,
243: "LBL_ExtractSC_Name")); // NOI18N
244: namePanel.add(nameLabel, java.awt.BorderLayout.WEST);
245: nameLabel
246: .getAccessibleContext()
247: .setAccessibleDescription(
248: org.openide.util.NbBundle
249: .getMessage(
250: ExtractSuperclassPanel.class,
251: "ExtractSuperclassPanel.nameLabel.AccessibleContext.accessibleDescription")); // NOI18N
252:
253: chooseLabel.setLabelFor(membersTable);
254: org.openide.awt.Mnemonics.setLocalizedText(chooseLabel,
255: org.openide.util.NbBundle.getMessage(
256: ExtractSuperclassPanel.class,
257: "LBL_ExtractSCLabel")); // NOI18N
258: chooseLabel.setBorder(javax.swing.BorderFactory
259: .createEmptyBorder(6, 0, 0, 0));
260: namePanel.add(chooseLabel, java.awt.BorderLayout.SOUTH);
261: chooseLabel
262: .getAccessibleContext()
263: .setAccessibleDescription(
264: org.openide.util.NbBundle
265: .getMessage(
266: ExtractSuperclassPanel.class,
267: "ExtractSuperclassPanel.chooseLabel.AccessibleContext.accessibleDescription")); // NOI18N
268:
269: namePanel.add(nameText, java.awt.BorderLayout.CENTER);
270: nameText.getAccessibleContext().setAccessibleName(null);
271: nameText.getAccessibleContext().setAccessibleDescription(null);
272:
273: add(namePanel, java.awt.BorderLayout.NORTH);
274:
275: membersTable.setModel(tableModel);
276: membersTable
277: .setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_NEXT_COLUMN);
278: scrollPane.setViewportView(membersTable);
279: membersTable.getAccessibleContext().setAccessibleName(null);
280: membersTable.getAccessibleContext().setAccessibleDescription(
281: null);
282:
283: add(scrollPane, java.awt.BorderLayout.CENTER);
284: }// </editor-fold>//GEN-END:initComponents
285:
286: // Variables declaration - do not modify//GEN-BEGIN:variables
287: private javax.swing.JLabel chooseLabel;
288: private javax.swing.JTable membersTable;
289: private javax.swing.JLabel nameLabel;
290: private javax.swing.JPanel namePanel;
291: private javax.swing.JTextField nameText;
292: private javax.swing.JScrollPane scrollPane;
293:
294: // End of variables declaration//GEN-END:variables
295:
296: // --- MODELS --------------------------------------------------------------
297:
298: /** Model for the members table.
299: */
300: private class TableModel extends AbstractTableModel {
301: TableModel() {
302: initialize();
303: }
304:
305: public int getColumnCount() {
306: return COLUMN_NAMES.length;
307: }
308:
309: public String getColumnName(int column) {
310: return UIUtilities
311: .getColumnName(NbBundle.getMessage(
312: ExtractSuperclassPanel.class,
313: COLUMN_NAMES[column]));
314: }
315:
316: public Class getColumnClass(int columnIndex) {
317: return COLUMN_CLASSES[columnIndex];
318: }
319:
320: public int getRowCount() {
321: return members.length;
322: }
323:
324: public Object getValueAt(int rowIndex, int columnIndex) {
325: return members[rowIndex][columnIndex];
326: }
327:
328: public void setValueAt(Object value, int rowIndex,
329: int columnIndex) {
330: members[rowIndex][columnIndex] = value;
331: }
332:
333: public boolean isCellEditable(int rowIndex, int columnIndex) {
334: if (columnIndex == 2) {
335: // column 2 is editable only in case of non-static methods
336: // if the target type is not an interface
337: if (members[rowIndex][2] == null) {
338: return false;
339: }
340: MemberInfo element = (MemberInfo) members[rowIndex][1];
341: return !(element.getModifiers().contains(
342: Modifier.STATIC) || element.getModifiers()
343: .contains(Modifier.ABSTRACT));
344: } else {
345: // column 0 is always editable, column 1 is never editable
346: return columnIndex == 0;
347: }
348: }
349:
350: private void initialize() {
351: final TreePathHandle sourceType = refactoring
352: .getSourceType();
353: if (sourceType == null)
354: return;
355:
356: FileObject fo = sourceType.getFileObject();
357: JavaSource js = JavaSource.forFileObject(fo);
358: try {
359: js.runUserActionTask(
360: new CancellableTask<CompilationController>() {
361: public void cancel() {
362: }
363:
364: public void run(CompilationController javac)
365: throws Exception {
366: javac
367: .toPhase(JavaSource.Phase.RESOLVED);
368: initializeInTransaction(javac,
369: sourceType);
370: }
371:
372: }, true);
373: } catch (IOException ex) {
374: new IllegalStateException(ex);
375: }
376: }
377:
378: private void initializeInTransaction(
379: CompilationController javac, TreePathHandle sourceType) {
380: TreePath sourceTreePath = sourceType.resolve(javac);
381: ClassTree sourceTree = (ClassTree) sourceTreePath.getLeaf();
382: List<MemberInfo<?>> result = new ArrayList<MemberInfo<?>>();
383:
384: for (Tree implTree : sourceTree.getImplementsClause()) {
385: TreePath implPath = javac.getTrees().getPath(
386: javac.getCompilationUnit(), implTree);
387: TypeMirror implMirror = javac.getTrees().getTypeMirror(
388: implPath);
389: result.add(MemberInfo.create(implMirror, implTree,
390: javac));
391: }
392:
393: for (Tree member : sourceTree.getMembers()) {
394: TreePath memberTreePath = javac.getTrees().getPath(
395: javac.getCompilationUnit(), member);
396: if (javac.getTreeUtilities()
397: .isSynthetic(memberTreePath))
398: continue;
399:
400: Element memberElm = javac.getTrees().getElement(
401: memberTreePath);
402: if (memberElm == null)
403: continue;
404:
405: if (memberElm.getModifiers().contains(Modifier.PRIVATE)) {
406: //ignore private members.
407: continue;
408: }
409: if (memberElm.getKind() == ElementKind.FIELD) {
410: result.add(MemberInfo.create(memberElm, javac));
411: } else if (memberElm.getKind() == ElementKind.METHOD) {
412: result.add(MemberInfo.create(memberElm, javac));
413: }
414: }
415:
416: // the members are collected
417: // now, create a tree map (to sort them) and create the table data
418: Collections.sort(result, new Comparator<MemberInfo<?>>() {
419: public int compare(MemberInfo<?> mi1, MemberInfo<?> mi2) {
420: int result = mi1.getGroup().compareTo(
421: mi2.getGroup());
422:
423: if (result == 0) {
424: result = mi1.getName().compareTo(mi2.getName());
425: }
426:
427: return result;
428: }
429: });
430: members = new Object[result.size()][3];
431: for (int i = 0; i < members.length; i++) {
432: members[i][0] = Boolean.FALSE;
433: MemberInfo<?> member = result.get(i);
434: members[i][1] = member;
435: if (member.getGroup() == MemberInfo.Group.METHOD) {
436: members[i][2] = member.isMakeAbstract();
437: } else {
438: members[i][2] = null;
439: }
440: }
441: // fire event to repaint the table
442: this .fireTableDataChanged();
443: }
444: }
445:
446: public Component getComponent() {
447: return this;
448: }
449: }
|