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