0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041: package org.netbeans.modules.java.hints.introduce;
0042:
0043: import com.sun.source.tree.AssignmentTree;
0044: import com.sun.source.tree.BinaryTree;
0045: import com.sun.source.tree.BlockTree;
0046: import com.sun.source.tree.BreakTree;
0047: import com.sun.source.tree.ClassTree;
0048: import com.sun.source.tree.CompoundAssignmentTree;
0049: import com.sun.source.tree.ContinueTree;
0050: import com.sun.source.tree.DoWhileLoopTree;
0051: import com.sun.source.tree.ExpressionTree;
0052: import com.sun.source.tree.ForLoopTree;
0053: import com.sun.source.tree.IdentifierTree;
0054: import com.sun.source.tree.IfTree;
0055: import com.sun.source.tree.MethodTree;
0056: import com.sun.source.tree.ModifiersTree;
0057: import com.sun.source.tree.NewClassTree;
0058: import com.sun.source.tree.ReturnTree;
0059: import com.sun.source.tree.Scope;
0060: import com.sun.source.tree.StatementTree;
0061: import com.sun.source.tree.Tree;
0062: import com.sun.source.tree.Tree.Kind;
0063: import com.sun.source.tree.TypeParameterTree;
0064: import com.sun.source.tree.UnaryTree;
0065: import com.sun.source.tree.VariableTree;
0066: import com.sun.source.tree.WhileLoopTree;
0067: import com.sun.source.util.SourcePositions;
0068: import com.sun.source.util.TreePath;
0069: import com.sun.source.util.TreePathScanner;
0070: import java.io.IOException;
0071: import java.util.Arrays;
0072: import java.util.Collections;
0073: import java.util.EnumMap;
0074: import java.util.EnumSet;
0075: import java.util.HashSet;
0076: import java.util.Iterator;
0077: import java.util.LinkedHashSet;
0078: import java.util.LinkedList;
0079: import java.util.List;
0080: import java.util.Map;
0081: import java.util.Set;
0082: import java.util.concurrent.atomic.AtomicBoolean;
0083: import javax.lang.model.element.Element;
0084: import javax.lang.model.element.ElementKind;
0085: import javax.lang.model.element.ExecutableElement;
0086: import javax.lang.model.element.Modifier;
0087: import javax.lang.model.element.TypeElement;
0088: import javax.lang.model.element.VariableElement;
0089: import javax.lang.model.type.DeclaredType;
0090: import javax.lang.model.type.TypeKind;
0091: import javax.lang.model.type.TypeMirror;
0092: import javax.swing.JButton;
0093: import javax.swing.text.BadLocationException;
0094: import javax.swing.text.Document;
0095: import org.netbeans.api.java.lexer.JavaTokenId;
0096: import org.netbeans.api.java.source.CancellableTask;
0097: import org.netbeans.api.java.source.Task;
0098: import org.netbeans.api.java.source.CompilationInfo;
0099: import org.netbeans.api.java.source.JavaSource;
0100: import org.netbeans.api.java.source.JavaSource.Phase;
0101: import org.netbeans.api.java.source.TreeMaker;
0102: import org.netbeans.api.java.source.TreePathHandle;
0103: import org.netbeans.api.java.source.TypeMirrorHandle;
0104: import org.netbeans.api.java.source.WorkingCopy;
0105: import org.netbeans.api.java.source.support.CaretAwareJavaSourceTaskFactory;
0106: import org.netbeans.api.java.source.support.SelectionAwareJavaSourceTaskFactory;
0107: import org.netbeans.api.lexer.TokenSequence;
0108: import org.netbeans.modules.java.editor.codegen.GeneratorUtils;
0109: import org.netbeans.modules.java.hints.errors.Utilities;
0110: import org.netbeans.spi.editor.hints.ChangeInfo;
0111: import org.netbeans.spi.editor.hints.ErrorDescription;
0112: import org.netbeans.spi.editor.hints.ErrorDescriptionFactory;
0113: import org.netbeans.spi.editor.hints.Fix;
0114: import org.netbeans.spi.editor.hints.HintsController;
0115: import org.netbeans.spi.editor.hints.Severity;
0116: import org.openide.DialogDescriptor;
0117: import org.openide.DialogDisplayer;
0118: import org.openide.filesystems.FileObject;
0119: import org.openide.util.NbBundle;
0120:
0121: /**
0122: *
0123: * @author Jan Lahoda
0124: */
0125: public class IntroduceHint implements CancellableTask<CompilationInfo> {
0126:
0127: private AtomicBoolean cancel = new AtomicBoolean();
0128:
0129: public IntroduceHint() {
0130: }
0131:
0132: private static final Set<TypeKind> NOT_ACCEPTED_TYPES = EnumSet.of(
0133: TypeKind.ERROR, TypeKind.NONE, TypeKind.OTHER,
0134: TypeKind.VOID, TypeKind.EXECUTABLE);
0135: private static final Set<JavaTokenId> WHITESPACES = EnumSet.of(
0136: JavaTokenId.WHITESPACE, JavaTokenId.BLOCK_COMMENT,
0137: JavaTokenId.LINE_COMMENT, JavaTokenId.JAVADOC_COMMENT);
0138:
0139: static int[] ignoreWhitespaces(CompilationInfo ci, int start,
0140: int end) {
0141: TokenSequence<JavaTokenId> ts = ci.getTokenHierarchy()
0142: .tokenSequence(JavaTokenId.language());
0143:
0144: if (ts == null) {
0145: return new int[] { start, end };
0146: }
0147:
0148: ts.move(start);
0149:
0150: if (ts.moveNext()) {
0151: boolean wasMoveNext = true;
0152:
0153: while (WHITESPACES.contains(ts.token().id())
0154: && (wasMoveNext = ts.moveNext()))
0155: ;
0156:
0157: if (wasMoveNext && ts.offset() > start)
0158: start = ts.offset();
0159: }
0160:
0161: ts.move(end);
0162:
0163: while (ts.movePrevious()
0164: && WHITESPACES.contains(ts.token().id())
0165: && ts.offset() < end)
0166: end = ts.offset();
0167:
0168: return new int[] { start, end };
0169: }
0170:
0171: static TreePath validateSelection(CompilationInfo ci, int start,
0172: int end) {
0173: TreePath tp = ci.getTreeUtilities().pathFor(
0174: (start + end) / 2 + 1);
0175:
0176: for (; tp != null; tp = tp.getParentPath()) {
0177: Tree leaf = tp.getLeaf();
0178:
0179: if (StatementTree.class.isAssignableFrom(leaf.getKind()
0180: .asInterface()))
0181: return null;
0182:
0183: if (!ExpressionTree.class.isAssignableFrom(leaf.getKind()
0184: .asInterface()))
0185: continue;
0186:
0187: long treeStart = ci.getTrees().getSourcePositions()
0188: .getStartPosition(ci.getCompilationUnit(), leaf);
0189: long treeEnd = ci.getTrees().getSourcePositions()
0190: .getEndPosition(ci.getCompilationUnit(), leaf);
0191:
0192: if (treeStart != start || treeEnd != end) {
0193: continue;
0194: }
0195:
0196: TypeMirror type = ci.getTrees().getTypeMirror(tp);
0197:
0198: if (type == null
0199: || NOT_ACCEPTED_TYPES.contains(type.getKind()))
0200: continue;
0201:
0202: if (tp.getParentPath().getLeaf().getKind() == Kind.EXPRESSION_STATEMENT)
0203: continue;
0204:
0205: if (tp.getLeaf().getKind() == Kind.ANNOTATION)
0206: continue;
0207:
0208: if (!isInsideClass(tp))
0209: return null;
0210:
0211: TreePath candidate = tp;
0212:
0213: tp = tp.getParentPath();
0214:
0215: while (tp != null) {
0216: switch (tp.getLeaf().getKind()) {
0217: case VARIABLE:
0218: VariableTree vt = (VariableTree) tp.getLeaf();
0219: if (vt.getInitializer() == leaf) {
0220: return candidate;
0221: } else {
0222: return null;
0223: }
0224: case NEW_CLASS:
0225: NewClassTree nct = (NewClassTree) tp.getLeaf();
0226:
0227: for (Tree p : nct.getArguments()) {
0228: if (p == leaf) {
0229: return candidate;
0230: }
0231: }
0232:
0233: return null;
0234: }
0235:
0236: leaf = tp.getLeaf();
0237: tp = tp.getParentPath();
0238: }
0239:
0240: return candidate;
0241: }
0242:
0243: return null;
0244: }
0245:
0246: static TreePathHandle validateSelectionForIntroduceMethod(
0247: CompilationInfo ci, int start, int end, int[] statementsSpan) {
0248: int[] span = ignoreWhitespaces(ci, Math.min(start, end), Math
0249: .max(start, end));
0250:
0251: start = span[0];
0252: end = span[1];
0253:
0254: if (start >= end)
0255: return null;
0256:
0257: TreePath tpStart = ci.getTreeUtilities().pathFor(start);
0258: TreePath tpEnd = ci.getTreeUtilities().pathFor(end);
0259:
0260: if (tpStart.getLeaf() != tpEnd.getLeaf()
0261: || tpStart.getLeaf().getKind() != Kind.BLOCK) {
0262: //??? not in the same block:
0263: return null;
0264: }
0265:
0266: int from = -1;
0267: int to = -1;
0268:
0269: BlockTree block = (BlockTree) tpStart.getLeaf();
0270: int index = 0;
0271:
0272: for (StatementTree s : block.getStatements()) {
0273: long sStart = ci.getTrees().getSourcePositions()
0274: .getStartPosition(ci.getCompilationUnit(), s);
0275:
0276: if (sStart == start) {
0277: from = index;
0278: }
0279:
0280: if (end < sStart && to == (-1)) {
0281: to = index - 1;
0282: }
0283:
0284: index++;
0285: }
0286:
0287: if (from == (-1)) {
0288: return null;
0289: }
0290:
0291: if (to == (-1))
0292: to = block.getStatements().size() - 1;
0293:
0294: if (to < from) {
0295: return null;
0296: }
0297:
0298: statementsSpan[0] = from;
0299: statementsSpan[1] = to;
0300:
0301: return TreePathHandle.create(tpStart, ci);
0302: }
0303:
0304: public void run(CompilationInfo info) {
0305: cancel.set(false);
0306:
0307: FileObject file = info.getFileObject();
0308: int[] selection = SelectionAwareJavaSourceTaskFactory
0309: .getLastSelection(file);
0310:
0311: if (selection == null) {
0312: //nothing to do....
0313: HintsController.setErrors(info.getFileObject(),
0314: IntroduceHint.class.getName(), Collections
0315: .<ErrorDescription> emptyList());
0316: } else {
0317: HintsController.setErrors(info.getFileObject(),
0318: IntroduceHint.class.getName(), computeError(info,
0319: selection[0], selection[1], null,
0320: new EnumMap<IntroduceKind, String>(
0321: IntroduceKind.class), cancel));
0322: }
0323: }
0324:
0325: public void cancel() {
0326: cancel.set(true);
0327: }
0328:
0329: private static boolean isConstructor(CompilationInfo info,
0330: TreePath path) {
0331: Element e = info.getTrees().getElement(path);
0332:
0333: return e != null && e.getKind() == ElementKind.CONSTRUCTOR;
0334: }
0335:
0336: private static List<TreePath> findConstructors(
0337: CompilationInfo info, TreePath method) {
0338: List<TreePath> result = new LinkedList<TreePath>();
0339: TreePath parent = method.getParentPath();
0340:
0341: if (parent.getLeaf().getKind() == Kind.CLASS) {
0342: for (Tree t : ((ClassTree) parent.getLeaf()).getMembers()) {
0343: TreePath tp = new TreePath(parent, t);
0344:
0345: if (isConstructor(info, tp)) {
0346: result.add(tp);
0347: }
0348: }
0349: }
0350:
0351: return result;
0352: }
0353:
0354: private static boolean isInsideClass(TreePath tp) {
0355: while (tp != null) {
0356: if (tp.getLeaf().getKind() == Kind.CLASS)
0357: return true;
0358:
0359: tp = tp.getParentPath();
0360: }
0361:
0362: return false;
0363: }
0364:
0365: static List<ErrorDescription> computeError(CompilationInfo info,
0366: int start, int end, Map<IntroduceKind, Fix> fixesMap,
0367: Map<IntroduceKind, String> errorMessage,
0368: AtomicBoolean cancel) {
0369: List<ErrorDescription> hints = new LinkedList<ErrorDescription>();
0370: List<Fix> fixes = new LinkedList<Fix>();
0371: TreePath resolved = validateSelection(info, start, end);
0372:
0373: if (resolved != null) {
0374: TreePathHandle h = TreePathHandle.create(resolved, info);
0375: TreePath method = findMethod(resolved);
0376: boolean isConstant = checkConstantExpression(info, resolved);
0377: boolean isVariable = findStatement(resolved) != null
0378: && method != null;
0379: List<TreePath> duplicatesForVariable = isVariable ? CopyFinder
0380: .computeDuplicates(info, resolved, method, cancel)
0381: : null;
0382: List<TreePath> duplicatesForConstant = /*isConstant ? */CopyFinder
0383: .computeDuplicates(info, resolved, new TreePath(
0384: info.getCompilationUnit()), cancel);// : null;
0385: Scope scope = info.getTrees().getScope(resolved);
0386: boolean statik = scope != null ? info.getTreeUtilities()
0387: .isStaticContext(scope) : false;
0388: String guessedName = Utilities.guessName(info, resolved);
0389: Fix variable = isVariable ? new IntroduceFix(h, info
0390: .getJavaSource(), guessedName,
0391: duplicatesForVariable.size() + 1,
0392: IntroduceKind.CREATE_VARIABLE) : null;
0393: Fix constant = isConstant ? new IntroduceFix(h, info
0394: .getJavaSource(), guessedName,
0395: duplicatesForConstant.size() + 1,
0396: IntroduceKind.CREATE_CONSTANT) : null;
0397: Fix field = null;
0398: Fix methodFix = null;
0399:
0400: if (method != null) {
0401: int[] initilizeIn = computeInitializeIn(info, resolved,
0402: duplicatesForConstant);
0403:
0404: if (statik) {
0405: initilizeIn[0] &= ~IntroduceFieldPanel.INIT_CONSTRUCTORS;
0406: initilizeIn[1] &= ~IntroduceFieldPanel.INIT_CONSTRUCTORS;
0407: }
0408:
0409: boolean allowFinalInCurrentMethod = false;
0410:
0411: if (isConstructor(info, method)) {
0412: //how many constructors do we have in the target class?:
0413: allowFinalInCurrentMethod = findConstructors(info,
0414: method).size() == 1;
0415: }
0416:
0417: field = new IntroduceFieldFix(h, info.getJavaSource(),
0418: guessedName, duplicatesForConstant.size() + 1,
0419: initilizeIn, statik, allowFinalInCurrentMethod);
0420:
0421: //introduce method based on expression:
0422: Element methodEl = info.getTrees().getElement(method);
0423: ScanStatement scanner = new ScanStatement(info,
0424: resolved.getLeaf(), resolved.getLeaf(), cancel);
0425:
0426: if (methodEl != null
0427: && (methodEl.getKind() == ElementKind.METHOD || methodEl
0428: .getKind() == ElementKind.CONSTRUCTOR)) {
0429: ExecutableElement ee = (ExecutableElement) methodEl;
0430:
0431: scanner.localVariables.addAll(ee.getParameters());
0432: }
0433:
0434: scanner.scan(method, null);
0435:
0436: List<TypeMirrorHandle> paramTypes = new LinkedList<TypeMirrorHandle>();
0437: List<String> paramNames = new LinkedList<String>();
0438:
0439: for (VariableElement ve : scanner.usedLocalVariables) {
0440: paramTypes
0441: .add(TypeMirrorHandle.create(ve.asType()));
0442: if (ve.getModifiers().contains(Modifier.FINAL)) {
0443: paramNames.add("!"
0444: + ve.getSimpleName().toString());
0445: } else {
0446: paramNames.add(ve.getSimpleName().toString());
0447: }
0448: }
0449:
0450: Set<TypeMirror> exceptions = new HashSet<TypeMirror>(
0451: info.getTreeUtilities().getUncaughtExceptions(
0452: resolved));
0453:
0454: Set<TypeMirrorHandle> exceptionHandles = new HashSet<TypeMirrorHandle>();
0455:
0456: for (TypeMirror tm : exceptions) {
0457: exceptionHandles.add(TypeMirrorHandle.create(tm));
0458: }
0459:
0460: methodFix = new IntroduceExpressionBasedMethodFix(info
0461: .getJavaSource(), h, paramTypes, paramNames,
0462: exceptionHandles);
0463: }
0464:
0465: if (fixesMap != null) {
0466: fixesMap.put(IntroduceKind.CREATE_VARIABLE, variable);
0467: fixesMap.put(IntroduceKind.CREATE_CONSTANT, constant);
0468: fixesMap.put(IntroduceKind.CREATE_FIELD, field);
0469: fixesMap.put(IntroduceKind.CREATE_METHOD, methodFix);
0470: }
0471:
0472: if (variable != null) {
0473: fixes.add(variable);
0474: }
0475:
0476: if (constant != null) {
0477: fixes.add(constant);
0478: }
0479:
0480: if (field != null) {
0481: fixes.add(field);
0482: }
0483:
0484: if (methodFix != null) {
0485: fixes.add(methodFix);
0486: }
0487: }
0488:
0489: Fix introduceMethod = computeIntroduceMethod(info, start, end,
0490: fixesMap, errorMessage, cancel);
0491:
0492: if (introduceMethod != null) {
0493: fixes.add(introduceMethod);
0494: if (fixesMap != null) {
0495: fixesMap.put(IntroduceKind.CREATE_METHOD,
0496: introduceMethod);
0497: }
0498: }
0499:
0500: if (!fixes.isEmpty()) {
0501: int pos = CaretAwareJavaSourceTaskFactory
0502: .getLastPosition(info.getFileObject());
0503: String displayName = NbBundle.getMessage(
0504: IntroduceHint.class, "HINT_Introduce");
0505:
0506: hints.add(ErrorDescriptionFactory.createErrorDescription(
0507: Severity.HINT, displayName, fixes, info
0508: .getFileObject(), pos, pos));
0509: }
0510:
0511: return hints;
0512: }
0513:
0514: static Fix computeIntroduceMethod(CompilationInfo info, int start,
0515: int end, Map<IntroduceKind, Fix> fixesMap,
0516: Map<IntroduceKind, String> errorMessage,
0517: AtomicBoolean cancel) {
0518: int[] statements = new int[2];
0519:
0520: TreePathHandle h = validateSelectionForIntroduceMethod(info,
0521: start, end, statements);
0522:
0523: if (h == null) {
0524: errorMessage.put(IntroduceKind.CREATE_METHOD,
0525: "ERR_Invalid_Selection"); // NOI18N
0526: return null;
0527: }
0528:
0529: TreePath block = h.resolve(info);
0530: TreePath method = findMethod(block);
0531: Element methodEl = info.getTrees().getElement(method);
0532: BlockTree bt = (BlockTree) block.getLeaf();
0533: List<? extends StatementTree> statementsToWrap = bt
0534: .getStatements().subList(statements[0],
0535: statements[1] + 1);
0536: ScanStatement scanner = new ScanStatement(info,
0537: statementsToWrap.get(0), statementsToWrap
0538: .get(statementsToWrap.size() - 1), cancel);
0539: Set<TypeMirror> exceptions = new HashSet<TypeMirror>();
0540: int index = 0;
0541: TypeMirror methodReturnType = info.getTypes().getNoType(
0542: TypeKind.VOID);
0543:
0544: if (methodEl != null
0545: && (methodEl.getKind() == ElementKind.METHOD || methodEl
0546: .getKind() == ElementKind.CONSTRUCTOR)) {
0547: ExecutableElement ee = (ExecutableElement) methodEl;
0548:
0549: scanner.localVariables.addAll(ee.getParameters());
0550: methodReturnType = ee.getReturnType();
0551: }
0552:
0553: scanner.scan(method, null);
0554:
0555: for (StatementTree s : bt.getStatements()) {
0556: TreePath path = new TreePath(block, s);
0557:
0558: if (index >= statements[0] && index <= statements[1]) {
0559: exceptions.addAll(info.getTreeUtilities()
0560: .getUncaughtExceptions(path));
0561: }
0562:
0563: index++;
0564: }
0565:
0566: ExitsFromAllBranches efab = new ExitsFromAllBranches(info);
0567:
0568: boolean exitsFromAllBranches = efab.scan(new TreePath(block,
0569: statementsToWrap.get(statementsToWrap.size() - 1)),
0570: null) == Boolean.TRUE;
0571:
0572: String exitsError = scanner.verifyExits(exitsFromAllBranches);
0573:
0574: if (exitsError != null) {
0575: errorMessage.put(IntroduceKind.CREATE_METHOD, exitsError);
0576: return null;
0577: }
0578:
0579: List<TypeMirrorHandle> paramTypes = new LinkedList<TypeMirrorHandle>();
0580: List<String> paramNames = new LinkedList<String>();
0581:
0582: for (VariableElement ve : scanner.usedLocalVariables) {
0583: paramTypes.add(TypeMirrorHandle.create(ve.asType()));
0584: if (ve.getModifiers().contains(Modifier.FINAL))
0585: paramNames.add("!" + ve.getSimpleName().toString());
0586: else
0587: paramNames.add(ve.getSimpleName().toString());
0588: }
0589:
0590: List<VariableElement> additionalLocalVariables = new LinkedList<VariableElement>(
0591: scanner.selectionWrittenLocalVariables);
0592:
0593: additionalLocalVariables.removeAll(scanner.usedLocalVariables);
0594: additionalLocalVariables
0595: .removeAll(scanner.selectionLocalVariables);
0596:
0597: List<TypeMirrorHandle> additionaLocalTypes = new LinkedList<TypeMirrorHandle>();
0598: List<String> additionaLocalNames = new LinkedList<String>();
0599:
0600: for (VariableElement ve : additionalLocalVariables) {
0601: additionaLocalTypes.add(TypeMirrorHandle
0602: .create(ve.asType()));
0603: additionaLocalNames.add(ve.getSimpleName().toString());
0604: }
0605:
0606: List<TreePathHandle> exits = new LinkedList<TreePathHandle>();
0607:
0608: for (TreePath tp : scanner.selectionExits) {
0609: exits.add(TreePathHandle.create(tp, info));
0610: }
0611:
0612: TypeMirror returnType;
0613: String returnName;
0614: boolean declareVariableForReturnValue;
0615:
0616: if (!scanner.usedSelectionLocalVariables.isEmpty()) {
0617: VariableElement result = scanner.usedSelectionLocalVariables
0618: .iterator().next();
0619:
0620: returnType = result.asType();
0621: returnName = result.getSimpleName().toString();
0622: declareVariableForReturnValue = scanner.selectionLocalVariables
0623: .contains(result);
0624: } else {
0625: if (!exits.isEmpty() && !exitsFromAllBranches) {
0626: returnType = info.getTypes().getPrimitiveType(
0627: TypeKind.BOOLEAN);
0628: returnName = null;
0629: declareVariableForReturnValue = false;
0630: } else {
0631: if (exitsFromAllBranches && scanner.hasReturns) {
0632: returnType = methodReturnType;
0633: returnName = null;
0634: declareVariableForReturnValue = false;
0635: } else {
0636: returnType = info.getTypes().getNoType(
0637: TypeKind.VOID);
0638: returnName = null;
0639: declareVariableForReturnValue = false;
0640: }
0641: }
0642: }
0643:
0644: Set<TypeMirrorHandle> exceptionHandles = new HashSet<TypeMirrorHandle>();
0645:
0646: for (TypeMirror tm : exceptions) {
0647: exceptionHandles.add(TypeMirrorHandle.create(tm));
0648: }
0649:
0650: return new IntroduceMethodFix(info.getJavaSource(), h,
0651: paramTypes, paramNames, additionaLocalTypes,
0652: additionaLocalNames, TypeMirrorHandle
0653: .create(returnType), returnName,
0654: declareVariableForReturnValue, exceptionHandles, exits,
0655: exitsFromAllBranches, statements[0], statements[1]);
0656: }
0657:
0658: static boolean checkConstantExpression(CompilationInfo info,
0659: TreePath path) {
0660: Tree expr = path.getLeaf();
0661:
0662: if (expr.getKind().asInterface() == BinaryTree.class) {
0663: BinaryTree bt = (BinaryTree) expr;
0664:
0665: return checkConstantExpression(info, new TreePath(path, bt
0666: .getLeftOperand()))
0667: && checkConstantExpression(info, new TreePath(path,
0668: bt.getRightOperand()));
0669: }
0670:
0671: if (expr.getKind() == Kind.IDENTIFIER
0672: || expr.getKind() == Kind.MEMBER_SELECT) {
0673: Element e = info.getTrees().getElement(path);
0674:
0675: if (e == null)
0676: return false;
0677:
0678: if (e.getKind() != ElementKind.FIELD)
0679: return false;
0680:
0681: if (!e.getModifiers().contains(Modifier.STATIC))
0682: return false;
0683:
0684: if (!e.getModifiers().contains(Modifier.FINAL))
0685: return false;
0686:
0687: TypeMirror type = e.asType();
0688:
0689: if (type.getKind().isPrimitive())
0690: return true;
0691:
0692: if (type.getKind() == TypeKind.DECLARED) {
0693: TypeElement te = (TypeElement) ((DeclaredType) type)
0694: .asElement();
0695:
0696: return "java.lang.String".equals(te.getQualifiedName()
0697: .toString()); // NOI18N
0698: }
0699:
0700: return false;
0701: }
0702:
0703: return LITERALS.contains(expr.getKind());
0704: }
0705:
0706: private static final Set<Kind> LITERALS = EnumSet.of(
0707: Kind.STRING_LITERAL, Kind.CHAR_LITERAL, Kind.INT_LITERAL,
0708: Kind.LONG_LITERAL, Kind.FLOAT_LITERAL, Kind.DOUBLE_LITERAL);
0709: private static final Set<ElementKind> LOCAL_VARIABLES = EnumSet.of(
0710: ElementKind.EXCEPTION_PARAMETER,
0711: ElementKind.LOCAL_VARIABLE, ElementKind.PARAMETER);
0712:
0713: private static TreePath findStatement(TreePath statementPath) {
0714: while (statementPath != null
0715: && (!StatementTree.class.isAssignableFrom(statementPath
0716: .getLeaf().getKind().asInterface()) || (statementPath
0717: .getParentPath() != null && statementPath
0718: .getParentPath().getLeaf().getKind() != Kind.BLOCK))) {
0719: if (statementPath.getLeaf().getKind() == Kind.CLASS)
0720: return null;
0721:
0722: statementPath = statementPath.getParentPath();
0723: }
0724:
0725: return statementPath;
0726: }
0727:
0728: private static TreePath findMethod(TreePath path) {
0729: while (path != null) {
0730: if (path.getLeaf().getKind() == Kind.METHOD) {
0731: return path;
0732: }
0733:
0734: if (path.getLeaf().getKind() == Kind.BLOCK
0735: && path.getParentPath() != null
0736: && path.getParentPath().getLeaf().getKind() == Kind.CLASS) {
0737: //initializer:
0738: return path;
0739: }
0740:
0741: path = path.getParentPath();
0742: }
0743:
0744: return null;
0745: }
0746:
0747: private static TreePath findClass(TreePath path) {
0748: while (path != null) {
0749: if (path.getLeaf().getKind() == Kind.CLASS) {
0750: return path;
0751: }
0752:
0753: path = path.getParentPath();
0754: }
0755:
0756: return null;
0757: }
0758:
0759: private static boolean isParentOf(TreePath parent, TreePath path) {
0760: Tree parentLeaf = parent.getLeaf();
0761:
0762: while (path != null && path.getLeaf() != parentLeaf) {
0763: path = path.getParentPath();
0764: }
0765:
0766: return path != null;
0767: }
0768:
0769: private static boolean isParentOf(TreePath parent,
0770: List<? extends TreePath> candidates) {
0771: for (TreePath tp : candidates) {
0772: if (!isParentOf(parent, tp))
0773: return false;
0774: }
0775:
0776: return true;
0777: }
0778:
0779: private static BlockTree findAddPosition(CompilationInfo info,
0780: TreePath original, List<? extends TreePath> candidates,
0781: int[] outPosition) {
0782: //find least common block holding all the candidates:
0783: TreePath statement = original;
0784:
0785: for (TreePath p : candidates) {
0786: Tree leaf = p.getLeaf();
0787: int leafStart = (int) info.getTrees().getSourcePositions()
0788: .getStartPosition(info.getCompilationUnit(), leaf);
0789: int stPathStart = (int) info.getTrees()
0790: .getSourcePositions().getStartPosition(
0791: info.getCompilationUnit(),
0792: statement.getLeaf());
0793:
0794: if (leafStart < stPathStart) {
0795: statement = p;
0796: }
0797: }
0798:
0799: List<TreePath> allCandidates = new LinkedList<TreePath>();
0800:
0801: allCandidates.add(original);
0802: allCandidates.addAll(candidates);
0803:
0804: statement = findStatement(statement);
0805:
0806: if (statement == null) {
0807: //XXX: well....
0808: return null;
0809: }
0810:
0811: while (statement.getParentPath() != null
0812: && !isParentOf(statement.getParentPath(), allCandidates)) {
0813: statement = statement.getParentPath();
0814: }
0815:
0816: //#126269: the common parent may not be block:
0817: while (statement.getParentPath() != null
0818: && statement.getParentPath().getLeaf().getKind() != Kind.BLOCK) {
0819: statement = statement.getParentPath();
0820: }
0821:
0822: if (statement.getParentPath() == null)
0823: return null;//XXX: log
0824:
0825: BlockTree statements = (BlockTree) statement.getParentPath()
0826: .getLeaf();
0827: StatementTree statementTree = (StatementTree) statement
0828: .getLeaf();
0829:
0830: int index = statements.getStatements().indexOf(statementTree);
0831:
0832: if (index == (-1)) {
0833: //really strange...
0834: return null;
0835: }
0836:
0837: outPosition[0] = index;
0838:
0839: return statements;
0840: }
0841:
0842: private static int[] computeInitializeIn(
0843: final CompilationInfo info, TreePath firstOccurrence,
0844: List<TreePath> occurrences) {
0845: int[] result = new int[] { 7, 7 };
0846: boolean inOneMethod = true;
0847: Tree currentMethod = findMethod(firstOccurrence).getLeaf();
0848:
0849: for (TreePath occurrence : occurrences) {
0850: TreePath method = findMethod(occurrence);
0851:
0852: if (method == null || currentMethod != method.getLeaf()) {
0853: inOneMethod = false;
0854: break;
0855: }
0856: }
0857:
0858: class Result extends RuntimeException {
0859: @Override
0860: public synchronized Throwable fillInStackTrace() {
0861: return null;
0862: }
0863:
0864: }
0865: class ReferencesLocalVariable extends
0866: TreePathScanner<Void, Void> {
0867: @Override
0868: public Void visitIdentifier(IdentifierTree node, Void p) {
0869: Element e = info.getTrees()
0870: .getElement(getCurrentPath());
0871:
0872: if (e != null && LOCAL_VARIABLES.contains(e.getKind())) {
0873: throw new Result();
0874: }
0875:
0876: return null;
0877: }
0878: }
0879:
0880: boolean referencesLocalvariables = false;
0881:
0882: try {
0883: new ReferencesLocalVariable().scan(firstOccurrence, null);
0884: } catch (Result r) {
0885: referencesLocalvariables = true;
0886: }
0887:
0888: if (!inOneMethod) {
0889: result[1] = IntroduceFieldPanel.INIT_FIELD
0890: | IntroduceFieldPanel.INIT_CONSTRUCTORS;
0891: }
0892:
0893: if (referencesLocalvariables) {
0894: result[0] = IntroduceFieldPanel.INIT_METHOD;
0895: result[1] = IntroduceFieldPanel.INIT_METHOD;
0896: }
0897:
0898: return result;
0899: }
0900:
0901: private static ExpressionTree expressionCopy(TreePath expression,
0902: WorkingCopy copy) throws IOException, BadLocationException {
0903: //hack: creating a copy of the expression:
0904: Document doc = copy.getDocument();
0905: int start = (int) copy.getTrees().getSourcePositions()
0906: .getStartPosition(copy.getCompilationUnit(),
0907: expression.getLeaf());
0908: int end = (int) copy.getTrees().getSourcePositions()
0909: .getEndPosition(copy.getCompilationUnit(),
0910: expression.getLeaf());
0911: String text = doc.getText(start, end - start);
0912:
0913: return copy.getTreeUtilities().parseExpression(text,
0914: new SourcePositions[1]);
0915: }
0916:
0917: private static List<ExpressionTree> realArguments(
0918: final TreeMaker make, List<String> parameterNames) {
0919: List<ExpressionTree> realArguments = new LinkedList<ExpressionTree>();
0920:
0921: for (String name : parameterNames) {
0922: name = name.startsWith("!") ? name.substring(1) : name;
0923: realArguments.add(make.Identifier(name));
0924: }
0925:
0926: return realArguments;
0927: }
0928:
0929: private static List<VariableTree> createVariables(WorkingCopy copy,
0930: List<TypeMirrorHandle> parameterTypes,
0931: List<String> parameterNames) {
0932: final TreeMaker make = copy.getTreeMaker();
0933: List<VariableTree> formalArguments = new LinkedList<VariableTree>();
0934: Iterator<TypeMirrorHandle> argType = parameterTypes.iterator();
0935: Iterator<String> argName = parameterNames.iterator();
0936:
0937: while (argType.hasNext() && argName.hasNext()) {
0938: TypeMirror tm = argType.next().resolve(copy);
0939:
0940: if (tm == null) {
0941: return null;
0942: }
0943:
0944: Tree type = make.Type(tm);
0945: String formalArgName = argName.next();
0946: Set<Modifier> formalArgMods = EnumSet
0947: .noneOf(Modifier.class);
0948:
0949: if (formalArgName.startsWith("!")) {
0950: formalArgName = formalArgName.substring(1);
0951: formalArgMods.add(Modifier.FINAL);
0952: }
0953:
0954: formalArguments.add(make.Variable(make
0955: .Modifiers(formalArgMods), formalArgName, type,
0956: null));
0957: }
0958:
0959: return formalArguments;
0960: }
0961:
0962: private static List<ExpressionTree> typeHandleToTree(
0963: WorkingCopy copy, Set<TypeMirrorHandle> thrownTypes) {
0964: final TreeMaker make = copy.getTreeMaker();
0965: List<ExpressionTree> thrown = new LinkedList<ExpressionTree>();
0966:
0967: for (TypeMirrorHandle h : thrownTypes) {
0968: TypeMirror t = h.resolve(copy);
0969:
0970: if (t == null) {
0971: return null;
0972: }
0973:
0974: thrown.add((ExpressionTree) make.Type(t));
0975: }
0976:
0977: return thrown;
0978: }
0979:
0980: private static final class ScanStatement extends
0981: TreePathScanner<Void, Void> {
0982: private static final int PHASE_BEFORE_SELECTION = 1;
0983: private static final int PHASE_INSIDE_SELECTION = 2;
0984: private static final int PHASE_AFTER_SELECTION = 3;
0985:
0986: private CompilationInfo info;
0987: private int phase = PHASE_BEFORE_SELECTION;
0988: private Tree firstInSelection;
0989: private Tree lastInSelection;
0990: private Set<VariableElement> localVariables = new HashSet<VariableElement>();
0991: private Set<VariableElement> usedLocalVariables = new LinkedHashSet<VariableElement>();
0992: private Set<VariableElement> selectionLocalVariables = new HashSet<VariableElement>();
0993: private Set<VariableElement> selectionWrittenLocalVariables = new HashSet<VariableElement>();
0994: private Set<VariableElement> usedSelectionLocalVariables = new HashSet<VariableElement>();
0995: private Set<TreePath> selectionExits = new HashSet<TreePath>();
0996: private Set<Tree> treesSeensInSelection = new HashSet<Tree>();
0997: private boolean hasReturns = false;
0998: private boolean hasBreaks = false;
0999: private boolean hasContinues = false;
1000: private boolean secondPass = false;
1001: private boolean stopSecondPass = false;
1002: private AtomicBoolean cancel;
1003:
1004: public ScanStatement(CompilationInfo info,
1005: Tree firstInSelection, Tree lastInSelection,
1006: AtomicBoolean cancel) {
1007: this .info = info;
1008: this .firstInSelection = firstInSelection;
1009: this .lastInSelection = lastInSelection;
1010: this .cancel = cancel;
1011: }
1012:
1013: @Override
1014: public Void scan(Tree tree, Void p) {
1015: if (stopSecondPass)
1016: return null;
1017:
1018: if (phase != PHASE_AFTER_SELECTION) {
1019: if (tree == firstInSelection) {
1020: phase = PHASE_INSIDE_SELECTION;
1021: }
1022:
1023: if (phase == PHASE_INSIDE_SELECTION) {
1024: treesSeensInSelection.add(tree);
1025: }
1026: }
1027:
1028: if (secondPass && tree == firstInSelection) {
1029: stopSecondPass = true;
1030: return null;
1031: }
1032:
1033: super .scan(tree, p);
1034:
1035: if (tree == lastInSelection) {
1036: phase = PHASE_AFTER_SELECTION;
1037: }
1038:
1039: return null;
1040: }
1041:
1042: @Override
1043: public Void visitVariable(VariableTree node, Void p) {
1044: Element e = info.getTrees().getElement(getCurrentPath());
1045:
1046: if (e != null && LOCAL_VARIABLES.contains(e.getKind())) {
1047: switch (phase) {
1048: case PHASE_BEFORE_SELECTION:
1049: localVariables.add((VariableElement) e);
1050: break;
1051: case PHASE_INSIDE_SELECTION:
1052: selectionLocalVariables.add((VariableElement) e);
1053: break;
1054: }
1055: }
1056:
1057: return super .visitVariable(node, p);
1058: }
1059:
1060: @Override
1061: public Void visitAssignment(AssignmentTree node, Void p) {
1062: if (phase == PHASE_INSIDE_SELECTION) {
1063: Element e = info.getTrees().getElement(
1064: new TreePath(getCurrentPath(), node
1065: .getVariable()));
1066:
1067: if (e != null && LOCAL_VARIABLES.contains(e.getKind())
1068: && localVariables.contains(e)) {
1069: selectionWrittenLocalVariables
1070: .add((VariableElement) e);
1071: }
1072: }
1073:
1074: //make sure the variable on the left side is not considered to be read:
1075: return scan(node.getExpression(), p);
1076: }
1077:
1078: @Override
1079: public Void visitCompoundAssignment(
1080: CompoundAssignmentTree node, Void p) {
1081: if (phase == PHASE_INSIDE_SELECTION) {
1082: Element e = info.getTrees().getElement(
1083: new TreePath(getCurrentPath(), node
1084: .getVariable()));
1085:
1086: if (e != null && LOCAL_VARIABLES.contains(e.getKind())) {
1087: selectionWrittenLocalVariables
1088: .add((VariableElement) e);
1089: }
1090: }
1091:
1092: return super .visitCompoundAssignment(node, p);
1093: }
1094:
1095: @Override
1096: public Void visitUnary(UnaryTree node, Void p) {
1097: Kind k = node.getKind();
1098:
1099: if (k == Kind.POSTFIX_DECREMENT
1100: || k == Kind.POSTFIX_INCREMENT
1101: || k == Kind.PREFIX_DECREMENT
1102: || k == Kind.PREFIX_INCREMENT) {
1103: //#109663:
1104: if (phase == PHASE_INSIDE_SELECTION) {
1105: Element e = info.getTrees().getElement(
1106: new TreePath(getCurrentPath(), node
1107: .getExpression()));
1108:
1109: if (e != null
1110: && LOCAL_VARIABLES.contains(e.getKind())) {
1111: selectionWrittenLocalVariables
1112: .add((VariableElement) e);
1113: }
1114: }
1115: }
1116: return super .visitUnary(node, p);
1117: }
1118:
1119: @Override
1120: public Void visitIdentifier(IdentifierTree node, Void p) {
1121: Element e = info.getTrees().getElement(getCurrentPath());
1122:
1123: if (e != null) {
1124: if (LOCAL_VARIABLES.contains(e.getKind())) {
1125: switch (phase) {
1126: case PHASE_INSIDE_SELECTION:
1127: if (localVariables.contains(e)) {
1128: usedLocalVariables.add((VariableElement) e);
1129: }
1130: break;
1131: case PHASE_AFTER_SELECTION:
1132: if (selectionLocalVariables.contains(e)
1133: || selectionWrittenLocalVariables
1134: .contains(e)) {
1135: usedSelectionLocalVariables
1136: .add((VariableElement) e);
1137: }
1138: break;
1139: }
1140: }
1141: }
1142: return super .visitIdentifier(node, p);
1143: }
1144:
1145: @Override
1146: public Void visitReturn(ReturnTree node, Void p) {
1147: if (phase == PHASE_INSIDE_SELECTION) {
1148: selectionExits.add(getCurrentPath());
1149: hasReturns = true;
1150: }
1151: return super .visitReturn(node, p);
1152: }
1153:
1154: @Override
1155: public Void visitBreak(BreakTree node, Void p) {
1156: if (phase == PHASE_INSIDE_SELECTION
1157: && !treesSeensInSelection.contains(info
1158: .getTreeUtilities().getBreakContinueTarget(
1159: getCurrentPath()))) {
1160: selectionExits.add(getCurrentPath());
1161: hasBreaks = true;
1162: }
1163: return super .visitBreak(node, p);
1164: }
1165:
1166: @Override
1167: public Void visitContinue(ContinueTree node, Void p) {
1168: if (phase == PHASE_INSIDE_SELECTION
1169: && !treesSeensInSelection.contains(info
1170: .getTreeUtilities().getBreakContinueTarget(
1171: getCurrentPath()))) {
1172: selectionExits.add(getCurrentPath());
1173: hasContinues = true;
1174: }
1175: return super .visitContinue(node, p);
1176: }
1177:
1178: @Override
1179: public Void visitWhileLoop(WhileLoopTree node, Void p) {
1180: super .visitWhileLoop(node, p);
1181:
1182: if (phase == PHASE_AFTER_SELECTION) {
1183: //#109663𛞨:
1184: //the selection was inside the while-loop, the variables inside the
1185: //condition&statement of the while loop need to be considered to be used again after the loop:
1186: secondPass = true;
1187: scan(node.getCondition(), p);
1188: scan(node.getStatement(), p);
1189: secondPass = false;
1190: stopSecondPass = false;
1191: }
1192:
1193: return null;
1194: }
1195:
1196: @Override
1197: public Void visitForLoop(ForLoopTree node, Void p) {
1198: super .visitForLoop(node, p);
1199:
1200: if (phase == PHASE_AFTER_SELECTION) {
1201: //#109663𛞨:
1202: //the selection was inside the for-loop, the variables inside the
1203: //condition, update and statement parts of the for loop need to be considered to be used again after the loop:
1204: secondPass = true;
1205: scan(node.getCondition(), p);
1206: scan(node.getUpdate(), p);
1207: scan(node.getStatement(), p);
1208: secondPass = false;
1209: stopSecondPass = false;
1210: }
1211:
1212: return null;
1213: }
1214:
1215: @Override
1216: public Void visitDoWhileLoop(DoWhileLoopTree node, Void p) {
1217: super .visitDoWhileLoop(node, p);
1218:
1219: if (phase == PHASE_AFTER_SELECTION) {
1220: //#109663𛞨:
1221: //the selection was inside the do-while, the variables inside the
1222: //statement part of the do-while loop need to be considered to be used again after the loop:
1223: secondPass = true;
1224: scan(node.getStatement(), p);
1225: secondPass = false;
1226: stopSecondPass = false;
1227: }
1228:
1229: return null;
1230: }
1231:
1232: private String verifyExits(boolean exitsFromAllBranches) {
1233: int i = 0;
1234:
1235: i += hasReturns ? 1 : 0;
1236: i += hasBreaks ? 1 : 0;
1237: i += hasContinues ? 1 : 0;
1238:
1239: if (i > 1) {
1240: return "ERR_Too_Many_Different_Exits"; // NOI18N
1241: }
1242:
1243: if ((exitsFromAllBranches ? 0 : i)
1244: + usedSelectionLocalVariables.size() > 1) {
1245: return "ERR_Too_Many_Return_Values"; // NOI18N
1246: }
1247:
1248: StatementTree breakOrContinueTarget = null;
1249: boolean returnValueComputed = false;
1250: TreePath returnValue = null;
1251:
1252: for (TreePath tp : selectionExits) {
1253: if (tp.getLeaf().getKind() == Kind.RETURN) {
1254: if (!exitsFromAllBranches) {
1255: ReturnTree rt = (ReturnTree) tp.getLeaf();
1256: TreePath currentReturnValue = rt
1257: .getExpression() != null ? new TreePath(
1258: tp, rt.getExpression())
1259: : null;
1260:
1261: if (!returnValueComputed) {
1262: returnValue = currentReturnValue;
1263: returnValueComputed = true;
1264: } else {
1265: if (returnValue != null
1266: && currentReturnValue != null) {
1267: List<TreePath> candidates = CopyFinder
1268: .computeDuplicates(info,
1269: returnValue,
1270: currentReturnValue,
1271: cancel);
1272:
1273: if (candidates.size() != 1
1274: || candidates.get(0).getLeaf() != rt
1275: .getExpression()) {
1276: return "ERR_Different_Return_Values"; // NOI18N
1277: }
1278: } else {
1279: if (returnValue != currentReturnValue) {
1280: return "ERR_Different_Return_Values"; // NOI18N
1281: }
1282: }
1283: }
1284: }
1285: } else {
1286: StatementTree target = info.getTreeUtilities()
1287: .getBreakContinueTarget(tp);
1288:
1289: if (breakOrContinueTarget == null) {
1290: breakOrContinueTarget = target;
1291: }
1292:
1293: if (breakOrContinueTarget != target)
1294: return "ERR_Break_Mismatch"; // NOI18N
1295: }
1296: }
1297:
1298: return null;
1299: }
1300: }
1301:
1302: private static final class ExitsFromAllBranches extends
1303: TreePathScanner<Boolean, Void> {
1304:
1305: private CompilationInfo info;
1306: private Set<Tree> seenTrees = new HashSet<Tree>();
1307:
1308: public ExitsFromAllBranches(CompilationInfo info) {
1309: this .info = info;
1310: }
1311:
1312: @Override
1313: public Boolean scan(Tree tree, Void p) {
1314: seenTrees.add(tree);
1315: return super .scan(tree, p);
1316: }
1317:
1318: @Override
1319: public Boolean visitIf(IfTree node, Void p) {
1320: return scan(node.getThenStatement(), null) == Boolean.TRUE
1321: && scan(node.getElseStatement(), null) == Boolean.TRUE;
1322: }
1323:
1324: @Override
1325: public Boolean visitReturn(ReturnTree node, Void p) {
1326: return true;
1327: }
1328:
1329: @Override
1330: public Boolean visitBreak(BreakTree node, Void p) {
1331: return !seenTrees.contains(info.getTreeUtilities()
1332: .getBreakContinueTarget(getCurrentPath()));
1333: }
1334:
1335: @Override
1336: public Boolean visitContinue(ContinueTree node, Void p) {
1337: return !seenTrees.contains(info.getTreeUtilities()
1338: .getBreakContinueTarget(getCurrentPath()));
1339: }
1340:
1341: @Override
1342: public Boolean visitClass(ClassTree node, Void p) {
1343: return false;
1344: }
1345:
1346: }
1347:
1348: private static final class IntroduceFix implements Fix {
1349:
1350: private String guessedName;
1351: private TreePathHandle handle;
1352: private JavaSource js;
1353: private int numDuplicates;
1354: private IntroduceKind kind;
1355:
1356: public IntroduceFix(TreePathHandle handle, JavaSource js,
1357: String guessedName, int numDuplicates,
1358: IntroduceKind kind) {
1359: this .handle = handle;
1360: this .js = js;
1361: this .guessedName = guessedName;
1362: this .numDuplicates = numDuplicates;
1363: this .kind = kind;
1364: }
1365:
1366: @Override
1367: public String toString() {
1368: return "[IntroduceFix:" + guessedName + ":" + numDuplicates
1369: + ":" + kind + "]"; // NOI18N
1370: }
1371:
1372: public String getKeyExt() {
1373: switch (kind) {
1374: case CREATE_CONSTANT:
1375: return "IntroduceConstant"; //NOI18N
1376: case CREATE_VARIABLE:
1377: return "IntroduceVariable"; //NOI18N
1378: default:
1379: throw new IllegalStateException();
1380: }
1381: }
1382:
1383: public String getText() {
1384: return NbBundle.getMessage(IntroduceHint.class, "FIX_"
1385: + getKeyExt()); //NOI18N
1386: }
1387:
1388: public ChangeInfo implement() throws IOException,
1389: BadLocationException {
1390: JButton btnOk = new JButton(NbBundle.getMessage(
1391: IntroduceHint.class, "LBL_Ok"));
1392: JButton btnCancel = new JButton(NbBundle.getMessage(
1393: IntroduceHint.class, "LBL_Cancel"));
1394: IntroduceVariablePanel panel = new IntroduceVariablePanel(
1395: numDuplicates, guessedName,
1396: kind == IntroduceKind.CREATE_CONSTANT, btnOk);
1397: String caption = NbBundle.getMessage(IntroduceHint.class,
1398: "CAP_" + getKeyExt()); //NOI18N
1399: DialogDescriptor dd = new DialogDescriptor(panel, caption,
1400: true, new Object[] { btnOk, btnCancel }, btnOk,
1401: DialogDescriptor.DEFAULT_ALIGN, null, null);
1402: if (DialogDisplayer.getDefault().notify(dd) != btnOk) {
1403: return null;//cancel
1404: }
1405: final String name = panel.getVariableName();
1406: final boolean replaceAll = panel.isReplaceAll();
1407: final boolean declareFinal = panel.isDeclareFinal();
1408: final Set<Modifier> access = kind == IntroduceKind.CREATE_CONSTANT ? panel
1409: .getAccess()
1410: : null;
1411: js.runModificationTask(new Task<WorkingCopy>() {
1412: public void run(WorkingCopy parameter) throws Exception {
1413: parameter.toPhase(Phase.RESOLVED);
1414:
1415: TreePath resolved = handle.resolve(parameter);
1416: TypeMirror tm = parameter.getTrees().getTypeMirror(
1417: resolved);
1418:
1419: if (resolved == null || tm == null) {
1420: return; //TODO...
1421: }
1422:
1423: tm = Utilities.resolveCapturedType(parameter, tm);
1424:
1425: //hack: creating a copy of the expression:
1426: ExpressionTree expressionCopy = expressionCopy(
1427: resolved, parameter);
1428: ModifiersTree mods;
1429: final TreeMaker make = parameter.getTreeMaker();
1430:
1431: switch (kind) {
1432: case CREATE_CONSTANT:
1433: //find first class:
1434: TreePath pathToClass = resolved;
1435:
1436: while (pathToClass != null
1437: && pathToClass.getLeaf().getKind() != Kind.CLASS) {
1438: pathToClass = pathToClass.getParentPath();
1439: }
1440:
1441: if (pathToClass == null) {
1442: return; //TODO...
1443: }
1444:
1445: Set<Modifier> localAccess = EnumSet.of(
1446: Modifier.FINAL, Modifier.STATIC);
1447:
1448: localAccess.addAll(access);
1449:
1450: mods = make.Modifiers(localAccess);
1451:
1452: VariableTree constant = make.Variable(mods,
1453: name, make.Type(tm), expressionCopy);
1454: ClassTree nueClass = GeneratorUtils
1455: .insertClassMember(parameter,
1456: pathToClass, constant);
1457:
1458: parameter.rewrite(pathToClass.getLeaf(),
1459: nueClass);
1460:
1461: if (replaceAll) {
1462: for (TreePath p : CopyFinder
1463: .computeDuplicates(
1464: parameter,
1465: resolved,
1466: new TreePath(
1467: parameter
1468: .getCompilationUnit()),
1469: new AtomicBoolean())) {
1470: parameter.rewrite(p.getLeaf(), make
1471: .Identifier(name));
1472: }
1473: }
1474: break;
1475: case CREATE_VARIABLE:
1476: TreePath method = findMethod(resolved);
1477:
1478: if (method == null) {
1479: return; //TODO...
1480: }
1481:
1482: BlockTree statements;
1483: int index;
1484:
1485: if (replaceAll) {
1486: List<TreePath> candidates = CopyFinder
1487: .computeDuplicates(parameter,
1488: resolved, method,
1489: new AtomicBoolean());
1490: for (TreePath p : candidates) {
1491: Tree leaf = p.getLeaf();
1492:
1493: parameter.rewrite(leaf, make
1494: .Identifier(name));
1495: }
1496:
1497: int[] out = new int[1];
1498: statements = findAddPosition(parameter,
1499: resolved, candidates, out);
1500:
1501: if (statements == null) {
1502: return;
1503: }
1504:
1505: index = out[0];
1506: } else {
1507: int[] out = new int[1];
1508: statements = findAddPosition(parameter,
1509: resolved, Collections
1510: .<TreePath> emptyList(),
1511: out);
1512:
1513: if (statements == null) {
1514: return;
1515: }
1516:
1517: index = out[0];
1518: }
1519:
1520: List<StatementTree> nueStatements = new LinkedList<StatementTree>(
1521: statements.getStatements());
1522: mods = make.Modifiers(declareFinal ? EnumSet
1523: .of(Modifier.FINAL) : EnumSet
1524: .noneOf(Modifier.class));
1525:
1526: nueStatements
1527: .add(
1528: index,
1529: make
1530: .Variable(mods, name,
1531: make.Type(tm),
1532: expressionCopy/*(ExpressionTree) resolved.getLeaf()*//*(ExpressionTree) resolved.getLeaf()*/));
1533:
1534: BlockTree nueBlock = make.Block(nueStatements,
1535: false);
1536:
1537: parameter.rewrite(statements, nueBlock);
1538: break;
1539: }
1540:
1541: parameter.rewrite(resolved.getLeaf(), make
1542: .Identifier(name));
1543: }
1544: }).commit();
1545: return null;
1546: }
1547: }
1548:
1549: private static final class IntroduceFieldFix implements Fix {
1550:
1551: private String guessedName;
1552: private TreePathHandle handle;
1553: private JavaSource js;
1554: private int numDuplicates;
1555: private int[] initilizeIn;
1556: private boolean statik;
1557: private boolean allowFinalInCurrentMethod;
1558:
1559: public IntroduceFieldFix(TreePathHandle handle, JavaSource js,
1560: String guessedName, int numDuplicates,
1561: int[] initilizeIn, boolean statik,
1562: boolean allowFinalInCurrentMethod) {
1563: this .handle = handle;
1564: this .js = js;
1565: this .guessedName = guessedName;
1566: this .numDuplicates = numDuplicates;
1567: this .initilizeIn = initilizeIn;
1568: this .statik = statik;
1569: this .allowFinalInCurrentMethod = allowFinalInCurrentMethod;
1570: }
1571:
1572: public String getText() {
1573: return NbBundle.getMessage(IntroduceHint.class,
1574: "FIX_IntroduceField");
1575: }
1576:
1577: @Override
1578: public String toString() {
1579: return "[IntroduceField:" + guessedName + ":"
1580: + numDuplicates + ":" + statik + ":"
1581: + allowFinalInCurrentMethod + ":"
1582: + Arrays.toString(initilizeIn) + "]"; // NOI18N
1583: }
1584:
1585: public ChangeInfo implement() throws IOException,
1586: BadLocationException {
1587: JButton btnOk = new JButton(NbBundle.getMessage(
1588: IntroduceHint.class, "LBL_Ok"));
1589: btnOk.getAccessibleContext().setAccessibleDescription(
1590: NbBundle.getMessage(IntroduceHint.class,
1591: "AD_IntrHint_OK"));
1592: JButton btnCancel = new JButton(NbBundle.getMessage(
1593: IntroduceHint.class, "LBL_Cancel"));
1594: btnCancel.getAccessibleContext().setAccessibleDescription(
1595: NbBundle.getMessage(IntroduceHint.class,
1596: "AD_IntrHint_Cancel"));
1597: IntroduceFieldPanel panel = new IntroduceFieldPanel(
1598: guessedName, initilizeIn, numDuplicates,
1599: allowFinalInCurrentMethod, btnOk);
1600: String caption = NbBundle.getMessage(IntroduceHint.class,
1601: "CAP_IntroduceField");
1602: DialogDescriptor dd = new DialogDescriptor(panel, caption,
1603: true, new Object[] { btnOk, btnCancel }, btnOk,
1604: DialogDescriptor.DEFAULT_ALIGN, null, null);
1605: if (DialogDisplayer.getDefault().notify(dd) != btnOk) {
1606: return null;//cancel
1607: }
1608: final String name = panel.getFieldName();
1609: final boolean replaceAll = panel.isReplaceAll();
1610: final boolean declareFinal = panel.isDeclareFinal();
1611: final Set<Modifier> access = panel.getAccess();
1612: final int initializeIn = panel.getInitializeIn();
1613: js.runModificationTask(new Task<WorkingCopy>() {
1614: public void run(WorkingCopy parameter) throws Exception {
1615: parameter.toPhase(Phase.RESOLVED);
1616:
1617: TreePath resolved = handle.resolve(parameter);
1618: TypeMirror tm = parameter.getTrees().getTypeMirror(
1619: resolved);
1620:
1621: if (resolved == null || tm == null) {
1622: return; //TODO...
1623: }
1624:
1625: tm = Utilities.resolveCapturedType(parameter, tm);
1626:
1627: TreePath pathToClass = resolved;
1628:
1629: while (pathToClass != null
1630: && pathToClass.getLeaf().getKind() != Kind.CLASS) {
1631: pathToClass = pathToClass.getParentPath();
1632: }
1633:
1634: if (pathToClass == null) {
1635: return; //TODO...
1636: }
1637:
1638: //hack: creating a copy of the expression:
1639: Document doc = parameter.getDocument();
1640: int start = (int) parameter.getTrees()
1641: .getSourcePositions().getStartPosition(
1642: parameter.getCompilationUnit(),
1643: resolved.getLeaf());
1644: int end = (int) parameter.getTrees()
1645: .getSourcePositions().getEndPosition(
1646: parameter.getCompilationUnit(),
1647: resolved.getLeaf());
1648: String text = doc.getText(start, end - start);
1649: ExpressionTree expressionCopy = parameter
1650: .getTreeUtilities().parseExpression(text,
1651: new SourcePositions[1]);
1652:
1653: Set<Modifier> mods = declareFinal ? EnumSet
1654: .of(Modifier.FINAL) : EnumSet
1655: .noneOf(Modifier.class);
1656:
1657: if (statik) {
1658: mods.add(Modifier.STATIC);
1659: }
1660:
1661: mods.addAll(access);
1662: final TreeMaker make = parameter.getTreeMaker();
1663:
1664: boolean isAnyOccurenceStatic = false;
1665:
1666: if (replaceAll) {
1667: for (TreePath p : CopyFinder.computeDuplicates(
1668: parameter, resolved,
1669: new TreePath(parameter
1670: .getCompilationUnit()),
1671: new AtomicBoolean())) {
1672: parameter.rewrite(p.getLeaf(), make
1673: .Identifier(name));
1674: Scope occurenceScope = parameter.getTrees()
1675: .getScope(p);
1676: if (parameter.getTreeUtilities()
1677: .isStaticContext(occurenceScope))
1678: isAnyOccurenceStatic = true;
1679:
1680: }
1681: }
1682:
1683: if (!statik && isAnyOccurenceStatic) {
1684: mods.add(Modifier.STATIC);
1685: }
1686:
1687: ModifiersTree modsTree = make.Modifiers(mods);
1688:
1689: VariableTree field = make
1690: .Variable(
1691: modsTree,
1692: name,
1693: make.Type(tm),
1694: initializeIn == IntroduceFieldPanel.INIT_FIELD ? expressionCopy
1695: : null);
1696: ClassTree nueClass = GeneratorUtils
1697: .insertClassMember(parameter, pathToClass,
1698: field);
1699:
1700: parameter.rewrite(resolved.getLeaf(), make
1701: .Identifier(name));
1702:
1703: TreePath method = findMethod(resolved);
1704:
1705: if (method == null) {
1706: return; //TODO...
1707: }
1708:
1709: if (initializeIn == IntroduceFieldPanel.INIT_METHOD) {
1710: TreePath statementPath = resolved;
1711:
1712: statementPath = findStatement(statementPath);
1713:
1714: if (statementPath == null) {
1715: //XXX: well....
1716: return;
1717: }
1718:
1719: BlockTree statements = (BlockTree) statementPath
1720: .getParentPath().getLeaf();
1721: StatementTree statement = (StatementTree) statementPath
1722: .getLeaf();
1723:
1724: int index = statements.getStatements().indexOf(
1725: statement);
1726:
1727: if (index == (-1)) {
1728: //really strange...
1729: return;
1730: }
1731:
1732: List<StatementTree> nueStatements = new LinkedList<StatementTree>(
1733: statements.getStatements());
1734:
1735: nueStatements.add(index, make
1736: .ExpressionStatement(make.Assignment(
1737: make.Identifier(name),
1738: expressionCopy)));
1739:
1740: BlockTree nueBlock = make.Block(nueStatements,
1741: false);
1742:
1743: parameter.rewrite(statements, nueBlock);
1744: }
1745:
1746: if (initializeIn == IntroduceFieldPanel.INIT_CONSTRUCTORS) {
1747: for (TreePath constructor : findConstructors(
1748: parameter, method)) {
1749: //check for syntetic constructor:
1750: if (parameter.getTreeUtilities()
1751: .isSynthetic(constructor)) {
1752: List<StatementTree> nueStatements = new LinkedList<StatementTree>();
1753: ExpressionTree reference = make
1754: .Identifier(name);
1755: Element clazz = parameter.getTrees()
1756: .getElement(pathToClass);
1757: ModifiersTree constrMods = clazz
1758: .getKind() != ElementKind.ENUM ? make
1759: .Modifiers(EnumSet
1760: .of(Modifier.PUBLIC))
1761: : make
1762: .Modifiers(Collections.EMPTY_SET);
1763:
1764: nueStatements
1765: .add(make
1766: .ExpressionStatement(make
1767: .Assignment(
1768: reference,
1769: expressionCopy)));
1770:
1771: BlockTree nueBlock = make.Block(
1772: nueStatements, false);
1773: MethodTree nueConstr = make
1774: .Method(
1775: constrMods,
1776: "<init>",
1777: null,
1778: Collections
1779: .<TypeParameterTree> emptyList(),
1780: Collections
1781: .<VariableTree> emptyList(),
1782: Collections
1783: .<ExpressionTree> emptyList(),
1784: nueBlock, null); //NOI18N
1785:
1786: nueClass = GeneratorUtils
1787: .insertClassMember(
1788: parameter,
1789: new TreePath(
1790: new TreePath(
1791: parameter
1792: .getCompilationUnit()),
1793: nueClass),
1794: nueConstr);
1795:
1796: nueClass = make
1797: .removeClassMember(nueClass,
1798: constructor.getLeaf());
1799: break;
1800: }
1801:
1802: boolean hasParameterOfTheSameName = false;
1803: MethodTree constr = ((MethodTree) constructor
1804: .getLeaf());
1805:
1806: for (VariableTree p : constr
1807: .getParameters()) {
1808: if (name.equals(p.getName().toString())) {
1809: hasParameterOfTheSameName = true;
1810: break;
1811: }
1812: }
1813:
1814: BlockTree origBody = constr.getBody();
1815: List<StatementTree> nueStatements = new LinkedList<StatementTree>();
1816: ExpressionTree reference = hasParameterOfTheSameName ? make
1817: .MemberSelect(make
1818: .Identifier("this"), name)
1819: : make.Identifier(name); // NOI18N
1820:
1821: List<? extends StatementTree> origStatements = origBody
1822: .getStatements();
1823: StatementTree canBeSuper = origStatements
1824: .get(0);
1825: if (!parameter.getTreeUtilities()
1826: .isSynthetic(
1827: TreePath.getPath(
1828: constructor,
1829: canBeSuper))) {
1830: nueStatements.add(canBeSuper);
1831: }
1832: nueStatements.add(make
1833: .ExpressionStatement(make
1834: .Assignment(reference,
1835: expressionCopy)));
1836: nueStatements.addAll(origStatements
1837: .subList(1, origStatements.size()));
1838:
1839: BlockTree nueBlock = make.Block(
1840: nueStatements, false);
1841:
1842: parameter.rewrite(origBody, nueBlock);
1843: }
1844: }
1845:
1846: parameter.rewrite(pathToClass.getLeaf(), nueClass);
1847: }
1848: }).commit();
1849: return null;
1850: }
1851: }
1852:
1853: private static final class IntroduceMethodFix implements Fix {
1854:
1855: private JavaSource js;
1856:
1857: private TreePathHandle parentBlock;
1858: private List<TypeMirrorHandle> parameterTypes;
1859: private List<String> parameterNames;
1860: private List<TypeMirrorHandle> additionalLocalTypes;
1861: private List<String> additionalLocalNames;
1862: private TypeMirrorHandle returnType;
1863: private String returnName;
1864: private boolean declareVariableForReturnValue;
1865: private Set<TypeMirrorHandle> thrownTypes;
1866: private List<TreePathHandle> exists;
1867: private boolean exitsFromAllBranches;
1868: private int from;
1869: private int to;
1870:
1871: public IntroduceMethodFix(JavaSource js,
1872: TreePathHandle parentBlock,
1873: List<TypeMirrorHandle> parameterTypes,
1874: List<String> parameterNames,
1875: List<TypeMirrorHandle> additionalLocalTypes,
1876: List<String> additionalLocalNames,
1877: TypeMirrorHandle returnType, String returnName,
1878: boolean declareVariableForReturnValue,
1879: Set<TypeMirrorHandle> thrownTypes,
1880: List<TreePathHandle> exists,
1881: boolean exitsFromAllBranches, int from, int to) {
1882: this .js = js;
1883: this .parentBlock = parentBlock;
1884: this .parameterTypes = parameterTypes;
1885: this .parameterNames = parameterNames;
1886: this .additionalLocalTypes = additionalLocalTypes;
1887: this .additionalLocalNames = additionalLocalNames;
1888: this .returnType = returnType;
1889: this .returnName = returnName;
1890: this .declareVariableForReturnValue = declareVariableForReturnValue;
1891: this .thrownTypes = thrownTypes;
1892: this .exists = exists;
1893: this .exitsFromAllBranches = exitsFromAllBranches;
1894: this .from = from;
1895: this .to = to;
1896: }
1897:
1898: public String getText() {
1899: return NbBundle.getMessage(IntroduceHint.class,
1900: "FIX_IntroduceMethod");
1901: }
1902:
1903: public String toDebugString(CompilationInfo info) {
1904: return "[IntroduceMethod:" + from + ":" + to + "]"; // NOI18N
1905: }
1906:
1907: public ChangeInfo implement() throws Exception {
1908: JButton btnOk = new JButton(NbBundle.getMessage(
1909: IntroduceHint.class, "LBL_Ok"));
1910: JButton btnCancel = new JButton(NbBundle.getMessage(
1911: IntroduceHint.class, "LBL_Cancel"));
1912: IntroduceMethodPanel panel = new IntroduceMethodPanel(""); //NOI18N
1913: panel.setOkButton(btnOk);
1914: String caption = NbBundle.getMessage(IntroduceHint.class,
1915: "CAP_IntroduceMethod");
1916: DialogDescriptor dd = new DialogDescriptor(panel, caption,
1917: true, new Object[] { btnOk, btnCancel }, btnOk,
1918: DialogDescriptor.DEFAULT_ALIGN, null, null);
1919: if (DialogDisplayer.getDefault().notify(dd) != btnOk) {
1920: return null;//cancel
1921: }
1922: final String name = panel.getMethodName();
1923: final Set<Modifier> access = panel.getAccess();
1924:
1925: js.runModificationTask(new Task<WorkingCopy>() {
1926: public void run(WorkingCopy copy) throws Exception {
1927: copy.toPhase(Phase.RESOLVED);
1928:
1929: TreePath block = parentBlock.resolve(copy);
1930: TypeMirror returnType = IntroduceMethodFix.this .returnType
1931: .resolve(copy);
1932:
1933: if (block == null || returnType == null) {
1934: return; //TODO...
1935: }
1936:
1937: Scope s = copy.getTrees().getScope(block);
1938: boolean isStatic = copy.getTreeUtilities()
1939: .isStaticContext(s);
1940: BlockTree statements = (BlockTree) block.getLeaf();
1941: List<StatementTree> nueStatements = new LinkedList<StatementTree>();
1942:
1943: nueStatements.addAll(statements.getStatements()
1944: .subList(0, from));
1945:
1946: final TreeMaker make = copy.getTreeMaker();
1947: List<ExpressionTree> realArguments = realArguments(
1948: make, parameterNames);
1949:
1950: List<StatementTree> methodStatements = new LinkedList<StatementTree>();
1951:
1952: Iterator<TypeMirrorHandle> additionalType = additionalLocalTypes
1953: .iterator();
1954: Iterator<String> additionalName = additionalLocalNames
1955: .iterator();
1956:
1957: while (additionalType.hasNext()
1958: && additionalName.hasNext()) {
1959: TypeMirror tm = additionalType.next().resolve(
1960: copy);
1961:
1962: if (tm == null) {
1963: //XXX:
1964: return;
1965: }
1966:
1967: Tree type = make.Type(tm);
1968:
1969: methodStatements.add(make.Variable(make
1970: .Modifiers(EnumSet
1971: .noneOf(Modifier.class)),
1972: additionalName.next(), type, null));
1973: }
1974:
1975: methodStatements.addAll(statements.getStatements()
1976: .subList(from, to + 1));
1977:
1978: Tree returnTypeTree = make.Type(returnType);
1979: ExpressionTree invocation = make.MethodInvocation(
1980: Collections.<ExpressionTree> emptyList(),
1981: make.Identifier(name), realArguments);
1982:
1983: ReturnTree ret = null;
1984:
1985: if (returnName != null) {
1986: ret = make.Return(make.Identifier(returnName));
1987: if (declareVariableForReturnValue) {
1988: nueStatements.add(make.Variable(make
1989: .Modifiers(EnumSet
1990: .noneOf(Modifier.class)),
1991: returnName, returnTypeTree,
1992: invocation));
1993: invocation = null;
1994: } else {
1995: invocation = make
1996: .Assignment(make
1997: .Identifier(returnName),
1998: invocation);
1999: }
2000: }
2001:
2002: if (!exists.isEmpty()) {
2003: TreePath handle = null;
2004:
2005: handle = exists.iterator().next().resolve(copy);
2006:
2007: if (handle == null) {
2008: return; //TODO...
2009: }
2010:
2011: assert handle != null;
2012:
2013: if (exitsFromAllBranches
2014: && handle.getLeaf().getKind() == Kind.RETURN) {
2015: nueStatements.add(make.Return(invocation));
2016: } else {
2017: if (ret == null) {
2018: if (exitsFromAllBranches) {
2019: ret = make.Return(null);
2020: } else {
2021: ret = make.Return(make
2022: .Literal(true));
2023: }
2024: }
2025:
2026: for (TreePathHandle h : exists) {
2027: TreePath resolved = h.resolve(copy);
2028:
2029: if (resolved == null) {
2030: return; //TODO...
2031: }
2032:
2033: copy.rewrite(resolved.getLeaf(), ret);
2034: }
2035:
2036: StatementTree branch = null;
2037:
2038: switch (handle.getLeaf().getKind()) {
2039: case BREAK:
2040: branch = make.Break(((BreakTree) handle
2041: .getLeaf()).getLabel());
2042: break;
2043: case CONTINUE:
2044: branch = make
2045: .Continue(((ContinueTree) handle
2046: .getLeaf()).getLabel());
2047: break;
2048: case RETURN:
2049: branch = make
2050: .Return(((ReturnTree) handle
2051: .getLeaf())
2052: .getExpression());
2053: break;
2054: }
2055:
2056: if (returnName != null
2057: || exitsFromAllBranches) {
2058: nueStatements
2059: .add(make
2060: .ExpressionStatement(invocation));
2061: nueStatements.add(branch);
2062: } else {
2063: nueStatements.add(make.If(make
2064: .Parenthesized(invocation),
2065: branch, null));
2066: methodStatements.add(make.Return(make
2067: .Literal(false)));
2068: }
2069: }
2070:
2071: invocation = null;
2072: } else {
2073: if (ret != null) {
2074: methodStatements.add(ret);
2075: }
2076: }
2077:
2078: if (invocation != null)
2079: nueStatements.add(make
2080: .ExpressionStatement(invocation));
2081:
2082: nueStatements.addAll(statements.getStatements()
2083: .subList(to + 1,
2084: statements.getStatements().size()));
2085:
2086: Set<Modifier> modifiers = EnumSet
2087: .noneOf(Modifier.class);
2088:
2089: if (isStatic) {
2090: modifiers.add(Modifier.STATIC);
2091: }
2092:
2093: modifiers.addAll(access);
2094:
2095: ModifiersTree mods = make.Modifiers(modifiers);
2096: List<VariableTree> formalArguments = createVariables(
2097: copy, parameterTypes, parameterNames);
2098:
2099: if (formalArguments == null) {
2100: return; //XXX
2101: }
2102:
2103: List<ExpressionTree> thrown = typeHandleToTree(
2104: copy, thrownTypes);
2105:
2106: if (thrownTypes == null) {
2107: return; //XXX
2108: }
2109:
2110: MethodTree method = make.Method(mods, name,
2111: returnTypeTree, Collections
2112: .<TypeParameterTree> emptyList(),
2113: formalArguments, thrown, make.Block(
2114: methodStatements, false), null);
2115:
2116: TreePath pathToClass = findClass(block);
2117:
2118: assert pathToClass != null;
2119:
2120: ClassTree nueClass = GeneratorUtils
2121: .insertClassMember(copy, pathToClass,
2122: method);
2123:
2124: copy.rewrite(pathToClass.getLeaf(), nueClass);
2125: copy.rewrite(statements, make.Block(nueStatements,
2126: statements.isStatic()));
2127: }
2128: }).commit();
2129:
2130: return null;
2131: }
2132:
2133: }
2134:
2135: private static final class IntroduceExpressionBasedMethodFix
2136: implements Fix {
2137:
2138: private JavaSource js;
2139:
2140: private TreePathHandle expression;
2141: private List<TypeMirrorHandle> parameterTypes;
2142: private List<String> parameterNames;
2143: private Set<TypeMirrorHandle> thrownTypes;
2144:
2145: public IntroduceExpressionBasedMethodFix(JavaSource js,
2146: TreePathHandle expression,
2147: List<TypeMirrorHandle> parameterTypes,
2148: List<String> parameterNames,
2149: Set<TypeMirrorHandle> thrownTypes) {
2150: this .js = js;
2151: this .expression = expression;
2152: this .parameterTypes = parameterTypes;
2153: this .parameterNames = parameterNames;
2154: this .thrownTypes = thrownTypes;
2155: }
2156:
2157: public String getText() {
2158: return NbBundle.getMessage(IntroduceHint.class,
2159: "FIX_IntroduceMethod");
2160: }
2161:
2162: public String toString() {
2163: return "[IntroduceExpressionBasedMethodFix]"; // NOI18N
2164: }
2165:
2166: public ChangeInfo implement() throws Exception {
2167: JButton btnOk = new JButton(NbBundle.getMessage(
2168: IntroduceHint.class, "LBL_Ok"));
2169: JButton btnCancel = new JButton(NbBundle.getMessage(
2170: IntroduceHint.class, "LBL_Cancel"));
2171: IntroduceMethodPanel panel = new IntroduceMethodPanel(""); //NOI18N
2172: panel.setOkButton(btnOk);
2173: String caption = NbBundle.getMessage(IntroduceHint.class,
2174: "CAP_IntroduceMethod");
2175: DialogDescriptor dd = new DialogDescriptor(panel, caption,
2176: true, new Object[] { btnOk, btnCancel }, btnOk,
2177: DialogDescriptor.DEFAULT_ALIGN, null, null);
2178: if (DialogDisplayer.getDefault().notify(dd) != btnOk) {
2179: return null;//cancel
2180: }
2181: final String name = panel.getMethodName();
2182: final Set<Modifier> access = panel.getAccess();
2183:
2184: js.runModificationTask(new Task<WorkingCopy>() {
2185: public void run(WorkingCopy copy) throws Exception {
2186: copy.toPhase(Phase.RESOLVED);
2187:
2188: TreePath expression = IntroduceExpressionBasedMethodFix.this .expression
2189: .resolve(copy);
2190: TypeMirror returnType = expression != null ? copy
2191: .getTrees().getTypeMirror(expression)
2192: : null;
2193:
2194: if (expression == null || returnType == null) {
2195: return; //TODO...
2196: }
2197:
2198: returnType = Utilities.resolveCapturedType(copy,
2199: returnType);
2200: ExpressionTree expressionCopy = expressionCopy(
2201: expression, copy);
2202:
2203: final TreeMaker make = copy.getTreeMaker();
2204: Tree returnTypeTree = make.Type(returnType);
2205: List<ExpressionTree> realArguments = realArguments(
2206: make, parameterNames);
2207:
2208: ExpressionTree invocation = make.MethodInvocation(
2209: Collections.<ExpressionTree> emptyList(),
2210: make.Identifier(name), realArguments);
2211:
2212: Scope s = copy.getTrees().getScope(expression);
2213: boolean isStatic = copy.getTreeUtilities()
2214: .isStaticContext(s);
2215:
2216: Set<Modifier> modifiers = EnumSet
2217: .noneOf(Modifier.class);
2218:
2219: if (isStatic) {
2220: modifiers.add(Modifier.STATIC);
2221: }
2222:
2223: modifiers.addAll(access);
2224:
2225: ModifiersTree mods = make.Modifiers(modifiers);
2226: List<VariableTree> formalArguments = createVariables(
2227: copy, parameterTypes, parameterNames);
2228:
2229: if (formalArguments == null) {
2230: return; //XXX
2231: }
2232:
2233: List<ExpressionTree> thrown = typeHandleToTree(
2234: copy, thrownTypes);
2235:
2236: if (thrownTypes == null) {
2237: return; //XXX
2238: }
2239:
2240: List<StatementTree> methodStatements = new LinkedList<StatementTree>();
2241:
2242: methodStatements.add(make.Return(expressionCopy));
2243:
2244: MethodTree method = make.Method(mods, name,
2245: returnTypeTree, Collections
2246: .<TypeParameterTree> emptyList(),
2247: formalArguments, thrown, make.Block(
2248: methodStatements, false), null);
2249: TreePath pathToClass = findClass(expression);
2250:
2251: assert pathToClass != null;
2252:
2253: ClassTree nueClass = GeneratorUtils
2254: .insertClassMember(copy, pathToClass,
2255: method);
2256:
2257: copy.rewrite(pathToClass.getLeaf(), nueClass);
2258: copy.rewrite(expression.getLeaf(), invocation);
2259: }
2260: }).commit();
2261:
2262: return null;
2263: }
2264:
2265: }
2266:
2267: }
|