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: package org.netbeans.modules.refactoring.java.ui;
042:
043: import java.awt.Component;
044: import java.awt.Dimension;
045: import java.io.IOException;
046: import java.util.ArrayList;
047: import java.util.Iterator;
048: import java.util.List;
049: import java.util.Set;
050: import java.util.logging.Logger;
051: import javax.lang.model.element.Element;
052: import javax.lang.model.element.ElementKind;
053: import javax.lang.model.element.Modifier;
054: import javax.lang.model.element.TypeElement;
055: import javax.lang.model.type.TypeMirror;
056: import javax.lang.model.util.ElementFilter;
057: import javax.swing.JPanel;
058: import javax.swing.JTable;
059: import javax.swing.UIManager;
060: import javax.swing.event.ChangeListener;
061: import javax.swing.event.TableModelEvent;
062: import javax.swing.event.TableModelListener;
063: import javax.swing.table.AbstractTableModel;
064: import org.netbeans.api.java.source.CancellableTask;
065: import org.netbeans.api.java.source.CompilationController;
066: import org.netbeans.api.java.source.CompilationInfo;
067: import org.netbeans.api.java.source.ElementHandle;
068: import org.netbeans.api.java.source.JavaSource;
069: import org.netbeans.api.java.source.TreePathHandle;
070: import org.netbeans.api.java.source.TreePathHandle;
071: import org.netbeans.api.java.source.UiUtils;
072: import org.netbeans.modules.refactoring.java.RetoucheUtils;
073: import org.netbeans.modules.refactoring.java.api.MemberInfo;
074: import org.netbeans.modules.refactoring.java.api.PushDownRefactoring;
075: import org.netbeans.modules.refactoring.java.ui.tree.ElementGripFactory;
076: import org.netbeans.modules.refactoring.spi.ui.CustomRefactoringPanel;
077: import org.openide.util.NbBundle;
078: import org.openide.util.lookup.Lookups;
079:
080: /**
081: *
082: * @author Pavel Flaska
083: */
084: public class PushDownPanel extends JPanel implements
085: CustomRefactoringPanel {
086:
087: // helper constants describing columns in the table of members
088: private static final String[] COLUMN_NAMES = {
089: "LBL_PullUp_Selected", "LBL_PullUp_Member",
090: "LBL_PushDown_KeepAbstract" }; // NOI18N
091: private static final Class[] COLUMN_CLASSES = { Boolean.class,
092: MemberInfo.class, Boolean.class };
093:
094: // refactoring this panel provides parameters for
095:
096: // refactoring this panel provides parameters for
097:
098: // refactoring this panel provides parameters for
099: private final PushDownRefactoring refactoring;
100: // table model for the table of members
101: // table model for the table of members
102: // table model for the table of members
103: private final TableModel tableModel;
104: // pre-selected members (comes from the refactoring action - the elements
105: // pre-selected members (comes from the refactoring action - the elements
106: // pre-selected members (comes from the refactoring action - the elements
107: // that should be pre-selected in the table of members)
108: private Set selectedMembers;
109: // target type to move the members to
110: // target type to move the members to
111: // target type to move the members to
112: private TreePathHandle originalType;
113: // data for the members table (first dimension - rows, second dimension - columns)
114: // data for the members table (first dimension - rows, second dimension - columns)
115: // data for the members table (first dimension - rows, second dimension - columns)
116: // the columns are: 0 = Selected (true/false), 1 = Member (Java element), 2 = Make Abstract (true/false)
117: private Object[][] members = new Object[0][0];
118:
119: private ElementKind sourceKind;
120: private ChangeListener parent;
121:
122: /** Creates new form PushDownPanel
123: * @param refactoring The refactoring this panel provides parameters for.
124: * @param selectedMembers Members that should be pre-selected in the panel
125: * (determined by which nodes the action was invoked on - e.g. if it was
126: * invoked on a method, the method will be pre-selected to be pulled up)
127: */
128: public PushDownPanel(PushDownRefactoring refactoring,
129: Set selectedMembers, ChangeListener parent) {
130: this .refactoring = refactoring;
131: this .tableModel = new TableModel();
132: this .selectedMembers = selectedMembers;
133: initComponents();
134: setPreferredSize(new Dimension(420, 380));
135: this .parent = parent;
136: }
137:
138: public void initialize() {
139: final TreePathHandle handle = refactoring.getSourceType();
140: JavaSource source = JavaSource.forFileObject(handle
141: .getFileObject());
142: try {
143: source.runUserActionTask(
144: new CancellableTask<CompilationController>() {
145: public void cancel() {
146: }
147:
148: public void run(CompilationController controller)
149: throws Exception {
150: controller
151: .toPhase(JavaSource.Phase.RESOLVED);
152: List<MemberInfo<? extends ElementHandle<? extends Element>>> l = new ArrayList();
153: TypeElement sourceTypeElement = (TypeElement) handle
154: .resolveElement(controller);
155: sourceKind = sourceTypeElement.getKind();
156: for (TypeMirror tm : sourceTypeElement
157: .getInterfaces()) {
158: l.add(MemberInfo.create(RetoucheUtils
159: .typeToElement(tm, controller),
160: controller,
161: MemberInfo.Group.IMPLEMENTS));
162: }
163: for (Element m : sourceTypeElement
164: .getEnclosedElements()) {
165: if (m.getKind() == ElementKind.CONSTRUCTOR
166: || m.getKind() == ElementKind.STATIC_INIT
167: || m.getKind() == ElementKind.INSTANCE_INIT) {
168: continue;
169: }
170: if (m instanceof TypeElement
171: && controller
172: .getTypes()
173: .isSubtype(
174: m.asType(),
175: sourceTypeElement
176: .asType())) {
177: continue;
178: }
179: l.add(MemberInfo.create(m, controller));
180: }
181:
182: Object[][] allMembers = new Object[l.size()][3];
183: int i = 0;
184: for (Iterator<MemberInfo<? extends ElementHandle<? extends Element>>> it = l
185: .iterator(); it.hasNext();) {
186: MemberInfo<? extends ElementHandle<? extends Element>> o = it
187: .next();
188: allMembers[i][0] = selectedMembers
189: .contains(o) ? Boolean.TRUE
190: : Boolean.FALSE;
191: allMembers[i][1] = o;
192: allMembers[i][2] = o.getElementHandle()
193: .getKind() == ElementKind.METHOD ? Boolean.FALSE
194: : null;
195: i++;
196: }
197: members = new Object[i][3];
198: if (i > 0)
199: System.arraycopy(allMembers, 0,
200: members, 0, i);
201:
202: // set renderer for the second column ("Member") do display name of the feature
203: membersTable
204: .setDefaultRenderer(
205: COLUMN_CLASSES[1],
206: new UIUtilities.JavaElementTableCellRenderer() {
207: // override the extractText method to add "implements " prefix to the text
208: // in case the value is instance of MultipartId (i.e. it represents an interface
209: // name from implements clause)
210: protected String extractText(
211: Object value) {
212: String displayValue = super
213: .extractText(value);
214:
215: if (value instanceof MemberInfo
216: && ((MemberInfo) value)
217: .getGroup() == MemberInfo.Group.IMPLEMENTS) {
218: displayValue = "implements "
219: + displayValue; // NOI18N
220: }
221: return displayValue;
222: }
223: });
224: // send renderer for the third column ("Make Abstract") to make the checkbox:
225: // 1. hidden for elements that are not methods
226: // 2. be disabled for static methods
227: // 3. be disabled and checked for methods if the target type is an interface
228: membersTable
229: .getColumnModel()
230: .getColumn(2)
231: .setCellRenderer(
232: new UIUtilities.BooleanTableCellRenderer() {
233: public Component getTableCellRendererComponent(
234: JTable table,
235: Object value,
236: boolean isSelected,
237: boolean hasFocus,
238: int row,
239: int column) {
240: // make the checkbox checked (even if "Make Abstract" is not set)
241: // for non-static methods if the target type is an interface
242: MemberInfo<ElementHandle> object = (MemberInfo) table
243: .getModel()
244: .getValueAt(
245: row,
246: 1);
247: // the super method automatically makes sure the checkbox is not visible if the
248: // "Make Abstract" value is null (which holds for non-methods)
249: // and that the checkbox is disabled if the cell is not editable (which holds for
250: // static methods all the time and for all methods in case the target type is an interface
251: // - see the table model)
252: return super
253: .getTableCellRendererComponent(
254: table,
255: value,
256: isSelected,
257: hasFocus,
258: row,
259: column);
260: }
261: });
262: // set background color of the scroll pane to be the same as the background
263: // of the table
264: membersScrollPane
265: .setBackground(membersTable
266: .getBackground());
267: membersScrollPane.getViewport()
268: .setBackground(
269: membersTable
270: .getBackground());
271: // set default row height
272: membersTable.setRowHeight(18);
273: // set grid color to be consistent with other netbeans tables
274: if (UIManager.getColor("control") != null) { // NOI18N
275: membersTable.setGridColor(UIManager
276: .getColor("control")); // NOI18N
277: }
278: // compute and set the preferred width for the first and the third column
279: UIUtilities.initColumnWidth(membersTable,
280: 0, Boolean.TRUE, 4);
281: UIUtilities.initColumnWidth(membersTable,
282: 2, Boolean.TRUE, 4);
283: }
284:
285: }, true);
286: } catch (IOException ioe) {
287: throw (RuntimeException) new RuntimeException()
288: .initCause(ioe);
289: }
290: parent.stateChanged(null);
291: }
292:
293: public MemberInfo[] getMembers() {
294: List list = new ArrayList();
295: // go through all rows of a table and collect selected members
296: for (int i = 0; i < members.length; i++) {
297: // if the current row is selected, create MemberInfo for it and
298: // add it to the list of selected members
299: if (members[i][0].equals(Boolean.TRUE)) {
300: Object element = members[i][1];
301: MemberInfo member;
302: member = (MemberInfo) element;
303: if (members[i][2] != null)
304: member.setMakeAbstract((Boolean) members[i][2]);
305: list.add(member);
306: }
307: }
308: // return the array of selected members
309: return (MemberInfo[]) list.toArray(new MemberInfo[list.size()]);
310: }
311:
312: /** This method is called from within the constructor to
313: * initialize the form.
314: * WARNING: Do NOT modify this code. The content of this method is
315: * always regenerated by the Form Editor.
316: */
317: // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
318: private void initComponents() {
319:
320: membersScrollPane = new javax.swing.JScrollPane();
321: membersTable = new javax.swing.JTable();
322: chooseLabel = new javax.swing.JLabel();
323:
324: setLayout(new java.awt.BorderLayout());
325:
326: membersScrollPane.setToolTipText("");
327:
328: membersTable.setModel(tableModel);
329: membersScrollPane.setViewportView(membersTable);
330: membersTable.getAccessibleContext().setAccessibleName(null);
331: membersTable.getAccessibleContext().setAccessibleDescription(
332: null);
333:
334: add(membersScrollPane, java.awt.BorderLayout.CENTER);
335:
336: chooseLabel.setLabelFor(membersTable);
337: org.openide.awt.Mnemonics.setLocalizedText(chooseLabel,
338: org.openide.util.NbBundle.getMessage(
339: PushDownPanel.class, "LBL_PushDownLabel")); // NOI18N
340: add(chooseLabel, java.awt.BorderLayout.NORTH);
341: }// </editor-fold>//GEN-END:initComponents
342:
343: // </editor-fold>
344: //GEN-FIRST:event_jComboBox1ActionPerformed
345: //GEN-LAST:event_jComboBox1ActionPerformed
346: // Variables declaration - do not modify//GEN-BEGIN:variables
347: private javax.swing.JLabel chooseLabel;
348: private javax.swing.JScrollPane membersScrollPane;
349: private javax.swing.JTable membersTable;
350:
351: // End of variables declaration//GEN-END:variables
352: private class TableModel extends AbstractTableModel {
353:
354: public int getColumnCount() {
355: return COLUMN_NAMES.length;
356: }
357:
358: public String getColumnName(int column) {
359: return UIUtilities.getColumnName(NbBundle.getMessage(
360: PushDownPanel.class, COLUMN_NAMES[column]));
361: }
362:
363: public Class getColumnClass(int columnIndex) {
364: return COLUMN_CLASSES[columnIndex];
365: }
366:
367: public int getRowCount() {
368: return members.length;
369: }
370:
371: public Object getValueAt(int rowIndex, int columnIndex) {
372: return members[rowIndex][columnIndex];
373: }
374:
375: public void setValueAt(Object value, int rowIndex,
376: int columnIndex) {
377: members[rowIndex][columnIndex] = value;
378: parent.stateChanged(null);
379: }
380:
381: public boolean isCellEditable(int rowIndex, int columnIndex) {
382: if (columnIndex == 2) {
383: // column 2 is editable only in case of non-static methods
384: if (members[rowIndex][2] == null) {
385: return false;
386: }
387: Object element = members[rowIndex][1];
388:
389: return !(((MemberInfo) element).getModifiers()
390: .contains(Modifier.STATIC));
391: } else {
392: // column 0 is always editable, column 1 is never editable
393: return columnIndex == 0;
394: }
395: }
396: }
397:
398: public Component getComponent() {
399: return this;
400: }
401: }
|