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.util.TreePath;
044: import java.awt.Component;
045: import java.awt.Dimension;
046: import java.io.IOException;
047: import java.util.ArrayList;
048: import java.util.Collection;
049: import java.util.Collections;
050: import java.util.Iterator;
051: import java.util.List;
052: import java.util.Set;
053: import javax.lang.model.element.Element;
054: import javax.lang.model.element.ElementKind;
055: import javax.lang.model.element.Modifier;
056: import javax.lang.model.element.TypeElement;
057: import javax.lang.model.element.VariableElement;
058: import javax.swing.DefaultComboBoxModel;
059: import javax.swing.JCheckBox;
060: import javax.swing.JPanel;
061: import javax.swing.JTable;
062: import javax.swing.UIManager;
063: import javax.swing.event.ChangeListener;
064: import javax.swing.event.TableModelEvent;
065: import javax.swing.event.TableModelListener;
066: import javax.swing.table.DefaultTableModel;
067: import javax.swing.table.TableCellRenderer;
068: import javax.swing.table.TableColumn;
069: import org.netbeans.api.java.source.CompilationController;
070: import org.netbeans.api.java.source.CompilationInfo;
071: import org.netbeans.api.java.source.Task;
072: import org.netbeans.api.java.source.JavaSource;
073: import org.netbeans.api.java.source.TreePathHandle;
074: import org.netbeans.modules.refactoring.java.api.MemberInfo;
075: import org.netbeans.modules.refactoring.java.plugins.EncapsulateFieldRefactoringPlugin;
076: import org.netbeans.modules.refactoring.java.ui.EncapsulateFieldsRefactoring.EncapsulateFieldInfo;
077: import org.netbeans.modules.refactoring.spi.ui.CustomRefactoringPanel;
078: import org.openide.util.NbBundle;
079:
080: /**
081: * Panel used by Encapsulate Field refactoring. Contains components to
082: * set parameters for the refactoring.
083: *
084: * @author Pavel Flaska
085: */
086: public final class EncapsulateFieldPanel extends JPanel implements
087: CustomRefactoringPanel {
088:
089: private DefaultTableModel model;
090: private TreePathHandle selectedObjects;
091: private ChangeListener parent;
092: private String classname;
093: private String[][] methodNames; // array of String {getterName, setterName}
094: private static boolean ALWAYS_USE_ACCESSORS = true;
095: private static int FIELD_ACCESS_INDEX = 3;
096: private static int METHOD_ACCESS_INDEX = 0;
097:
098: private static final String modifierNames[] = { "public", // NOI18N
099: "protected", // NOI18N
100: "<default>", // NOI18N
101: "private" // NOI18N
102: };
103:
104: private static final String[] columnNames = {
105: getString("LBL_ColField"), // NOI18N
106: " ", // NOI18N
107: getString("LBL_ColGetter"), // NOI18N
108: " ", // NOI18N
109: getString("LBL_ColSetter") // NOI18N
110: };
111:
112: // modifier items in combo - indexes
113: private static final int MOD_PUBLIC_INDEX = 0;
114: private static final int MOD_PROTECTED_INDEX = 1;
115: private static final int MOD_DEFAULT_INDEX = 2;
116: private static final int MOD_PRIVATE_INDEX = 3;
117:
118: private static final Class[] columnTypes = new Class[] {
119: MemberInfo.class, java.lang.Boolean.class,
120: java.lang.String.class, java.lang.Boolean.class,
121: java.lang.String.class };
122:
123: /**
124: * Creates new form EncapsulateFieldPanel.
125: *
126: * @param selectedObjects array of selected objects
127: */
128: public EncapsulateFieldPanel(TreePathHandle selectedObject,
129: ChangeListener parent) {
130: String title = getString("LBL_TitleEncapsulateFields");
131:
132: this .selectedObjects = selectedObject;
133: this .parent = parent;
134: model = new TabM(columnNames, 0);
135: initComponents();
136: setName(title);
137: jCheckAccess.setSelected(ALWAYS_USE_ACCESSORS);
138: jComboAccess.setSelectedIndex(METHOD_ACCESS_INDEX);
139: jComboField.setSelectedIndex(FIELD_ACCESS_INDEX);
140: // *** initialize table
141: // set renderer for the column "Field" to display name of the feature (with icon)
142: jTableFields.setDefaultRenderer(MemberInfo.class,
143: new UIUtilities.JavaElementTableCellRenderer());
144: // set background color of the scroll pane to be the same as the background
145: // of the table
146: jScrollField.setBackground(jTableFields.getBackground());
147: jScrollField.getViewport().setBackground(
148: jTableFields.getBackground());
149: // set default row height
150: jTableFields.setRowHeight(18);
151: // set grid color to be consistent with other netbeans tables
152: if (UIManager.getColor("control") != null) { // NOI18N
153: jTableFields.setGridColor(UIManager.getColor("control")); // NOI18N
154: }
155: }
156:
157: public Component getComponent() {
158: return this ;
159: }
160:
161: private boolean initialized = false;
162:
163: public void initialize() {
164: if (initialized)
165: return;
166:
167: JavaSource js = JavaSource.forFileObject(selectedObjects
168: .getFileObject());
169: try {
170: js.runUserActionTask(new Task<CompilationController>() {
171:
172: public void run(CompilationController javac)
173: throws Exception {
174: javac.toPhase(JavaSource.Phase.RESOLVED);
175: initialize(javac);
176: }
177: }, true);
178: } catch (IOException ex) {
179: throw new IllegalStateException(ex);
180: }
181: }
182:
183: public void initialize(CompilationController javac) {
184: TreePath selectedPath = selectedObjects.resolve(javac);
185: Element selectedElm = javac.getTrees().getElement(selectedPath);
186: List<String[]> names = new ArrayList<String[]>();
187:
188: for (VariableElement field : initFields(selectedPath, javac)) {
189: TreePath fieldTPath = javac.getTrees().getPath(field);
190: boolean createGetter = selectedElm == field;
191: boolean createSetter = createGetter
192: && !field.getModifiers().contains(Modifier.FINAL);
193: String[] getset = new String[] {
194: EncapsulateFieldRefactoringPlugin
195: .computeGetterName(field),
196: EncapsulateFieldRefactoringPlugin
197: .computeSetterName(field) };
198: names.add(getset);
199: model.addRow(new Object[] {
200: MemberInfo.create(fieldTPath, javac),
201: createGetter ? Boolean.TRUE : Boolean.FALSE,
202: createGetter ? getset[0] : null,
203: createSetter ? Boolean.TRUE : Boolean.FALSE,
204: createSetter ? getset[1] : null });
205: }
206:
207: this .methodNames = names.toArray(new String[names.size()][]);
208:
209: packRows(jTableFields);
210:
211: jTableFields.getTableHeader().setReorderingAllowed(false);
212: setColumnWidth(1);
213: setColumnWidth(3);
214:
215: jTableFields.invalidate();
216: jTableFields.repaint();
217: model.addTableModelListener(new TableModelListener() {
218: public void tableChanged(TableModelEvent e) {
219: int col = e.getColumn();
220: int row = e.getFirstRow();
221: if (col == 1 || col == 3) {
222: Boolean value = (Boolean) model
223: .getValueAt(row, col);
224: if (value.booleanValue()) {
225: if (col == 1) {
226: model.setValueAt(methodNames[row][0], row,
227: col + 1);
228: } else {
229: model.setValueAt(methodNames[row][1], row,
230: col + 1);
231: }
232: } else {
233: if (!(model.getValueAt(row, col + 1) == null))
234: model.setValueAt(null, row, col + 1);
235: }
236: } else {
237: String value = (String) model.getValueAt(row, col);
238: if (value == null | "".equals(value)) {
239: model.setValueAt(Boolean.FALSE, row, col - 1);
240: }
241: }
242: parent.stateChanged(null);
243: }
244: });
245:
246: initialized = true;
247: }
248:
249: private void setColumnWidth(int a) {
250: TableColumn col = jTableFields.getColumnModel().getColumn(a);
251: JCheckBox box = new JCheckBox();
252: int width = (int) box.getPreferredSize().getWidth();
253: col.setPreferredWidth(width);
254: col.setMinWidth(width);
255: col.setMaxWidth(width);
256: col.setResizable(false);
257: }
258:
259: private int getMinimumRowHeight(JTable table, int rowIndex) {
260: int height = table.getRowHeight();
261:
262: for (int c = 0; c < table.getColumnCount(); c++) {
263: TableCellRenderer renderer = table.getCellRenderer(
264: rowIndex, c);
265: Component comp = table.prepareRenderer(renderer, rowIndex,
266: c);
267: int h = comp.getMinimumSize().height;
268: height = Math.max(height, h);
269: }
270: return height;
271: }
272:
273: private void packRows(JTable table) {
274: int max = 0;
275: int h;
276: for (int r = 0; r < table.getRowCount(); r++) {
277: h = getMinimumRowHeight(table, r);
278: if (h > max)
279: max = h;
280: }
281: table.setRowHeight(max);
282: table.getTableHeader()
283: .setPreferredSize(
284: new Dimension(table.getTableHeader()
285: .getPreferredSize().width, max));
286: }
287:
288: /**
289: * Returns table model with data provided by user.
290: *
291: * @return data provided in table by user
292: */
293: protected DefaultTableModel getTableModel() {
294: return model;
295: }
296:
297: /** This method is called from within the constructor to
298: * initialize the form.
299: * WARNING: Do NOT modify this code. The content of this method is
300: * always regenerated by the Form Editor.
301: */
302: // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
303: private void initComponents() {
304: java.awt.GridBagConstraints gridBagConstraints;
305:
306: jLblTitle = new javax.swing.JLabel();
307: jScrollField = new javax.swing.JScrollPane();
308: jTableFields = new javax.swing.JTable();
309: jLblFieldVis = new javax.swing.JLabel();
310: jComboField = new javax.swing.JComboBox();
311: jLblAccessVis = new javax.swing.JLabel();
312: jComboAccess = new javax.swing.JComboBox();
313: jCheckAccess = new javax.swing.JCheckBox();
314:
315: setLayout(new java.awt.GridBagLayout());
316:
317: jLblTitle.setLabelFor(jTableFields);
318: org.openide.awt.Mnemonics.setLocalizedText(jLblTitle,
319: getString("LBL_FieldList"));
320: gridBagConstraints = new java.awt.GridBagConstraints();
321: gridBagConstraints.gridx = 0;
322: gridBagConstraints.gridy = 0;
323: gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
324: gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
325: add(jLblTitle, gridBagConstraints);
326:
327: jScrollField.setPreferredSize(new java.awt.Dimension(300, 200));
328: jTableFields.setModel(model);
329: jScrollField.setViewportView(jTableFields);
330: jTableFields
331: .getAccessibleContext()
332: .setAccessibleDescription(
333: java.util.ResourceBundle
334: .getBundle(
335: "org/netbeans/modules/refactoring/java/ui/Bundle")
336: .getString("ACSD_jTableFields"));
337:
338: gridBagConstraints = new java.awt.GridBagConstraints();
339: gridBagConstraints.gridx = 0;
340: gridBagConstraints.gridy = 1;
341: gridBagConstraints.gridwidth = 2;
342: gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
343: gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
344: gridBagConstraints.weightx = 1.0;
345: gridBagConstraints.weighty = 1.0;
346: gridBagConstraints.insets = new java.awt.Insets(2, 2, 12, 2);
347: add(jScrollField, gridBagConstraints);
348:
349: jLblFieldVis.setLabelFor(jComboField);
350: org.openide.awt.Mnemonics.setLocalizedText(jLblFieldVis,
351: getString("LBL_FieldVis"));
352: gridBagConstraints = new java.awt.GridBagConstraints();
353: gridBagConstraints.gridx = 0;
354: gridBagConstraints.gridy = 2;
355: gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
356: gridBagConstraints.insets = new java.awt.Insets(2, 2, 5, 5);
357: add(jLblFieldVis, gridBagConstraints);
358:
359: jComboField.setModel(new DefaultComboBoxModel(modifierNames));
360: jComboField.setSelectedIndex(3);
361: jComboField.setPreferredSize(new java.awt.Dimension(100, 23));
362: gridBagConstraints = new java.awt.GridBagConstraints();
363: gridBagConstraints.gridx = 1;
364: gridBagConstraints.gridy = 2;
365: gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
366: gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 0);
367: add(jComboField, gridBagConstraints);
368: jComboField
369: .getAccessibleContext()
370: .setAccessibleDescription(
371: java.util.ResourceBundle
372: .getBundle(
373: "org/netbeans/modules/refactoring/java/ui/Bundle")
374: .getString("ACSD_fieldModifiers"));
375:
376: jLblAccessVis.setLabelFor(jComboAccess);
377: org.openide.awt.Mnemonics.setLocalizedText(jLblAccessVis,
378: getString("LBL_AccessVis"));
379: gridBagConstraints = new java.awt.GridBagConstraints();
380: gridBagConstraints.gridx = 0;
381: gridBagConstraints.gridy = 3;
382: gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
383: gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 5);
384: add(jLblAccessVis, gridBagConstraints);
385:
386: jComboAccess.setModel(new DefaultComboBoxModel(modifierNames));
387: jComboAccess.setSelectedIndex(0);
388: jComboAccess.setPreferredSize(new java.awt.Dimension(100, 24));
389: gridBagConstraints = new java.awt.GridBagConstraints();
390: gridBagConstraints.gridx = 1;
391: gridBagConstraints.gridy = 3;
392: gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
393: add(jComboAccess, gridBagConstraints);
394: jComboAccess
395: .getAccessibleContext()
396: .setAccessibleDescription(
397: java.util.ResourceBundle
398: .getBundle(
399: "org/netbeans/modules/refactoring/java/ui/Bundle")
400: .getString("ACSD_methodAcc"));
401:
402: jCheckAccess.setSelected(true);
403: org.openide.awt.Mnemonics.setLocalizedText(jCheckAccess,
404: getString("LBL_AccessorsEven"));
405: // NOI18N
406: jCheckAccess.setMargin(new java.awt.Insets(12, 2, 2, 2));
407: gridBagConstraints = new java.awt.GridBagConstraints();
408: gridBagConstraints.gridx = 0;
409: gridBagConstraints.gridy = 4;
410: gridBagConstraints.gridwidth = 2;
411: gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
412: gridBagConstraints.insets = new java.awt.Insets(2, 0, 2, 0);
413: add(jCheckAccess, gridBagConstraints);
414: jCheckAccess
415: .getAccessibleContext()
416: .setAccessibleDescription(
417: java.util.ResourceBundle
418: .getBundle(
419: "org/netbeans/modules/refactoring/java/ui/Bundle")
420: .getString("ACSD_useAccessors"));
421:
422: }// </editor-fold>//GEN-END:initComponents
423:
424: // Variables declaration - do not modify//GEN-BEGIN:variables
425: private javax.swing.JCheckBox jCheckAccess;
426: private javax.swing.JComboBox jComboAccess;
427: private javax.swing.JComboBox jComboField;
428: private javax.swing.JLabel jLblAccessVis;
429: private javax.swing.JLabel jLblFieldVis;
430: private javax.swing.JLabel jLblTitle;
431: private javax.swing.JScrollPane jScrollField;
432: private javax.swing.JTable jTableFields;
433:
434: // End of variables declaration//GEN-END:variables
435:
436: private static String getString(String key) {
437: return NbBundle.getMessage(EncapsulateFieldPanel.class, key);
438: }
439:
440: /**
441: * Returns the array of all fields from class which contains
442: * selectedField provided as a parameter.
443: *
444: * @param selectedField field, whose class is used for obtaining
445: * array of fields.
446: * @return array of all fields in a class.
447: */
448: private List<VariableElement> initFields(TreePath selectedField,
449: CompilationInfo javac) {
450: Element elm = javac.getTrees().getElement(selectedField);
451: TypeElement encloser = null;
452: if (ElementKind.FIELD == elm.getKind()) {
453: encloser = (TypeElement) elm.getEnclosingElement();
454: } else {
455: encloser = (TypeElement) elm;
456: }
457:
458: List<VariableElement> result = new ArrayList<VariableElement>();
459: for (Element member : encloser.getEnclosedElements()) {
460: if (ElementKind.FIELD == member.getKind()) {
461: result.add((VariableElement) member);
462: }
463: }
464:
465: this .classname = encloser.getQualifiedName().toString();
466: final String title = " - " + classname; // NOI18N
467: setName(getName() + title);
468:
469: return result;
470: }
471:
472: public Collection<EncapsulateFieldInfo> getAllFields() {
473: List<EncapsulateFieldInfo> result = new ArrayList<EncapsulateFieldInfo>();
474: List rows = model.getDataVector();
475: for (Iterator rowIt = rows.iterator(); rowIt.hasNext();) {
476: List row = (List) rowIt.next();
477: if (row.get(1) == Boolean.TRUE
478: || row.get(3) == Boolean.TRUE) {
479: String getterName = (String) row.get(2);
480: String setterName = (String) row.get(4);
481: MemberInfo mi = (MemberInfo) row.get(0);
482: result.add(new EncapsulateFieldInfo((TreePathHandle) mi
483: .getElementHandle(),
484: "".equals(getterName) ? null : getterName, // NOI18N
485: "".equals(setterName) ? null : setterName)); // NOI18N
486: }
487: }
488:
489: return result;
490: }
491:
492: public boolean isCheckAccess() {
493: ALWAYS_USE_ACCESSORS = jCheckAccess.isSelected();
494: return ALWAYS_USE_ACCESSORS;
495: }
496:
497: public Set<Modifier> getFieldModifiers() {
498: FIELD_ACCESS_INDEX = jComboField.getSelectedIndex();
499: Modifier mod = getModifier(FIELD_ACCESS_INDEX);
500: if (mod == null) {
501: return Collections.EMPTY_SET;
502: } else {
503: return Collections.singleton(mod);
504: }
505: }
506:
507: public Set<Modifier> getMethodModifiers() {
508: METHOD_ACCESS_INDEX = jComboAccess.getSelectedIndex();
509: Modifier mod = getModifier(METHOD_ACCESS_INDEX);
510: if (mod == null) {
511: return Collections.EMPTY_SET;
512: } else {
513: return Collections.singleton(mod);
514: }
515: }
516:
517: private Modifier getModifier(int index) {
518: switch (index) {
519: case MOD_PRIVATE_INDEX:
520: return Modifier.PRIVATE;
521: case MOD_DEFAULT_INDEX:
522: return null; /* no modifier */
523: case MOD_PROTECTED_INDEX:
524: return Modifier.PROTECTED;
525: case MOD_PUBLIC_INDEX:
526: return Modifier.PUBLIC;
527: }
528: throw new IllegalStateException("unknown index: " + index); // NOI18N
529: }
530:
531: String getClassname() {
532: return classname;
533: }
534:
535: ////////////////////////////////////////////////////////////////////////////
536: // INNER CLASSES
537: ////////////////////////////////////////////////////////////////////////////
538: /**
539: * The class is used by EncapsulateFieldPanel - it represents table model
540: * used inside in jTable. It denies to edit first column, returns the
541: * column classes (Boolean, String, String, String) etc.
542: */
543: private static class TabM extends DefaultTableModel {
544:
545: public TabM(Object[] columnNames, int rowCount) {
546: super (columnNames, rowCount);
547: }
548:
549: /**
550: * Returns the appropriate class for column.
551: *
552: * @param columnIndex index of column for which we are looking for a class
553: * @return class which is used in the column
554: */
555: @Override
556: public Class getColumnClass(int columnIndex) {
557: return columnTypes[columnIndex];
558: }
559:
560: /**
561: * We deny edit the field column (index 1), because field can't
562: * be renamed when we encapsulate it.
563: *
564: * @param row doesn't matter
565: * @param column for value 1, it returns false, otherwise true
566: *
567: * @return true, if the cell is editable
568: */
569: @Override
570: public boolean isCellEditable(int row, int column) {
571: if (column == 0)
572: return false;
573: if (column == 1 || column == 3)
574: return true;
575: return ((Boolean) getValueAt(row, column - 1))
576: .booleanValue();
577: }
578: }
579: // end INNER CLASSES
580: }
|