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.plugins;
042:
043: import com.sun.source.util.TreePath;
044: import java.io.IOException;
045: import java.util.ArrayList;
046: import java.util.Collection;
047: import java.util.HashSet;
048: import java.util.List;
049: import java.util.Set;
050: import javax.lang.model.element.Element;
051: import javax.lang.model.element.ElementKind;
052: import javax.lang.model.element.Modifier;
053: import javax.lang.model.element.NestingKind;
054: import javax.lang.model.element.TypeElement;
055: import org.netbeans.api.java.source.CompilationController;
056: import org.netbeans.api.java.source.JavaSource;
057: import org.netbeans.api.java.source.TreePathHandle;
058: import org.netbeans.modules.refactoring.api.Problem;
059: import org.netbeans.modules.refactoring.spi.RefactoringElementsBag;
060: import org.netbeans.modules.refactoring.api.ProgressListener;
061: import org.netbeans.modules.refactoring.api.ProgressEvent;
062: import org.netbeans.modules.refactoring.java.RetoucheUtils;
063: import org.netbeans.modules.refactoring.java.api.EncapsulateFieldRefactoring;
064: import org.netbeans.modules.refactoring.java.plugins.EncapsulateFieldRefactoringPlugin.EncapsulateDesc;
065: import org.netbeans.modules.refactoring.java.plugins.EncapsulateFieldRefactoringPlugin.Encapsulator;
066: import org.netbeans.modules.refactoring.java.spi.JavaRefactoringPlugin;
067: import org.netbeans.modules.refactoring.java.ui.EncapsulateFieldsRefactoring;
068: import org.netbeans.modules.refactoring.java.ui.EncapsulateFieldsRefactoring.EncapsulateFieldInfo;
069: import org.openide.filesystems.FileObject;
070: import org.openide.util.NbBundle;
071:
072: /** Encapsulate fields refactoring. This is a composed refactoring (uses instances of {@link org.netbeans.modules.refactoring.api.EncapsulateFieldRefactoring}
073: * to encapsulate several fields at once.
074: *
075: * @author Pavel Flaska
076: * @author Jan Becicka
077: * @author Jan Pokorsky
078: */
079: public final class EncapsulateFieldsPlugin extends
080: JavaRefactoringPlugin {
081:
082: private List<EncapsulateFieldRefactoringPlugin> refactorings;
083: private final EncapsulateFieldsRefactoring refactoring;
084:
085: private ProgressListener listener = new ProgressListener() {
086: public void start(ProgressEvent event) {
087: fireProgressListenerStart(event.getOperationType(), event
088: .getCount());
089: }
090:
091: public void step(ProgressEvent event) {
092: fireProgressListenerStep();
093: }
094:
095: public void stop(ProgressEvent event) {
096: fireProgressListenerStop();
097: }
098: };
099:
100: /** Creates a new instance of EcapsulateFields.
101: * @param selectedObjects Array of objects (fields) that should be encapsulated.
102: */
103: public EncapsulateFieldsPlugin(
104: EncapsulateFieldsRefactoring refactoring) {
105: this .refactoring = refactoring;
106: }
107:
108: @Override
109: protected Problem checkParameters(CompilationController javac)
110: throws IOException {
111: return validation(2, javac);
112: }
113:
114: @Override
115: public Problem fastCheckParameters() {
116: Collection<EncapsulateFieldInfo> fields = refactoring
117: .getRefactorFields();
118: if (fields.isEmpty()) {
119: return new Problem(true, NbBundle.getMessage(
120: EncapsulateFieldsPlugin.class,
121: "ERR_EncapsulateNothingSelected"));
122: }
123: initRefactorings(fields, refactoring.getMethodModifiers(),
124: refactoring.getFieldModifiers(), refactoring
125: .isAlwaysUseAccessors());
126: try {
127: return validation(1, null);
128: } catch (IOException ex) {
129: throw new IllegalStateException(ex);
130: }
131: }
132:
133: @Override
134: protected Problem preCheck(CompilationController javac)
135: throws IOException {
136: javac.toPhase(JavaSource.Phase.RESOLVED);
137: TreePath selectedField = refactoring.getSelectedObject()
138: .resolve(javac);
139: if (selectedField == null) {
140: return new Problem(true, NbBundle.getMessage(
141: EncapsulateFieldsPlugin.class, "DSC_ElNotAvail"));
142: }
143:
144: Element elm = javac.getTrees().getElement(selectedField);
145: if (elm != null && ElementKind.FIELD == elm.getKind()) {
146: TreePath source = javac.getTrees().getPath(elm);
147: if (source == null) {
148: // missing sources with field declaration
149: return new Problem(true, NbBundle
150: .getMessage(EncapsulateFieldsPlugin.class,
151: "DSC_ElNotAvail"));
152: }
153:
154: TypeElement encloser = (TypeElement) elm
155: .getEnclosingElement();
156: if (ElementKind.INTERFACE == encloser.getKind()
157: || NestingKind.ANONYMOUS == encloser
158: .getNestingKind()) {
159: // interface constants, local variables and annonymous declarations are unsupported
160: return new Problem(true, NbBundle.getMessage(
161: EncapsulateFieldsPlugin.class,
162: "ERR_EncapsulateInIntf"));
163: }
164: return null;
165: }
166:
167: TreePath clazz = RetoucheUtils.findEnclosingClass(javac,
168: selectedField, true, false, true, false, false);
169: TypeElement clazzElm = (TypeElement) javac.getTrees()
170: .getElement(clazz);
171: if (elm != clazzElm || clazzElm == null) {
172: return new Problem(true, NbBundle.getMessage(
173: EncapsulateFieldsPlugin.class,
174: "ERR_EncapsulateWrongType"));
175: }
176: if (ElementKind.INTERFACE == clazzElm.getKind()
177: || ElementKind.ANNOTATION_TYPE == clazzElm.getKind()
178: || NestingKind.ANONYMOUS == clazzElm.getNestingKind()) {
179: return new Problem(true, NbBundle.getMessage(
180: EncapsulateFieldsPlugin.class,
181: "ERR_EncapsulateInIntf"));
182: }
183:
184: for (Element member : clazzElm.getEnclosedElements()) {
185: if (ElementKind.FIELD == member.getKind()) { // no enum constant
186: return null;
187: }
188: }
189: return new Problem(true, NbBundle.getMessage(
190: EncapsulateFieldsPlugin.class,
191: "ERR_EncapsulateNoFields", clazzElm.getQualifiedName()));
192: }
193:
194: public Problem prepare(RefactoringElementsBag elements) {
195: Problem problem = null;
196: Set<FileObject> references = new HashSet<FileObject>();
197: List<EncapsulateDesc> descs = new ArrayList<EncapsulateDesc>(
198: refactorings.size());
199: for (EncapsulateFieldRefactoringPlugin ref : refactorings) {
200: if (cancelRequest) {
201: return null;
202: }
203:
204: EncapsulateDesc desc = ref.prepareEncapsulator(problem);
205: problem = desc.p;
206: desc.p = null;
207: if (problem != null && problem.isFatal()) {
208: return problem;
209: }
210: descs.add(desc);
211: references.addAll(desc.refs);
212: }
213:
214: Encapsulator encapsulator = new Encapsulator(descs, problem);
215: createAndAddElements(references, new TransformTask(
216: encapsulator, descs.get(0).fieldHandle), elements,
217: refactoring);
218: problem = encapsulator.getProblem();
219: return problem;
220: }
221:
222: private void initRefactorings(
223: Collection<EncapsulateFieldInfo> refactorFields,
224: Set<Modifier> methodModifier, Set<Modifier> fieldModifier,
225: boolean alwaysUseAccessors) {
226: refactorings = new ArrayList<EncapsulateFieldRefactoringPlugin>(
227: refactorFields.size());
228: for (EncapsulateFieldInfo info : refactorFields) {
229: EncapsulateFieldRefactoring ref = new EncapsulateFieldRefactoring(
230: info.getField());
231: ref.setGetterName(info.getGetterName());
232: ref.setSetterName(info.getSetterName());
233: ref.setMethodModifiers(methodModifier);
234: ref.setFieldModifiers(fieldModifier);
235: ref.setAlwaysUseAccessors(alwaysUseAccessors);
236: refactorings
237: .add(new EncapsulateFieldRefactoringPlugin(ref));
238: }
239: }
240:
241: private Problem validation(int phase, CompilationController javac)
242: throws IOException {
243: Problem result = null;
244: for (EncapsulateFieldRefactoringPlugin ref : refactorings) {
245: Problem lastresult = null;
246: switch (phase) {
247: case 1:
248: lastresult = ref.fastCheckParameters();
249: break;
250: case 2:
251: lastresult = ref.preCheck(javac);
252: result = chainProblems(result, lastresult);
253: if (result != null && result.isFatal()) {
254: return result;
255: }
256: lastresult = ref.checkParameters(javac);
257: ref.addProgressListener(listener);
258: break;
259: }
260:
261: result = chainProblems(result, lastresult);
262: if (result != null && result.isFatal()) {
263: return result;
264: }
265:
266: }
267:
268: return result;
269: }
270:
271: private static Problem chainProblems(Problem oldp, Problem newp) {
272: if (oldp == null) {
273: return newp;
274: } else if (newp == null) {
275: return oldp;
276: } else if (newp.isFatal()) {
277: newp.setNext(oldp);
278: return newp;
279: } else {
280: // [TODO] performance
281: Problem p = oldp;
282: while (p.getNext() != null)
283: p = p.getNext();
284: p.setNext(newp);
285: return oldp;
286: }
287: }
288:
289: @Override
290: protected JavaSource getJavaSource(Phase p) {
291: TreePathHandle selectedField = refactoring.getSelectedObject();
292: FileObject fo = selectedField.getFileObject();
293: return JavaSource.forFileObject(fo);
294: }
295:
296: }
|