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: * Portions Copyrighted 2007 Sun Microsystems, Inc.
027: */
028: package org.netbeans.modules.java.hints.errors;
029:
030: import com.sun.source.tree.BlockTree;
031: import com.sun.source.tree.ClassTree;
032: import com.sun.source.tree.ExpressionTree;
033: import com.sun.source.tree.MethodTree;
034: import com.sun.source.tree.NewClassTree;
035: import com.sun.source.tree.StatementTree;
036: import com.sun.source.tree.TypeParameterTree;
037: import com.sun.source.tree.VariableTree;
038: import com.sun.source.util.TreePath;
039: import java.io.IOException;
040: import java.util.ArrayList;
041: import java.util.Collections;
042: import java.util.EnumSet;
043: import java.util.Iterator;
044: import java.util.List;
045: import java.util.Set;
046: import java.util.logging.Level;
047: import javax.lang.model.element.ElementKind;
048: import javax.lang.model.element.Modifier;
049: import javax.lang.model.element.TypeElement;
050: import javax.lang.model.type.TypeKind;
051: import javax.lang.model.type.TypeMirror;
052: import org.netbeans.api.java.source.Task;
053: import org.netbeans.api.java.source.ClasspathInfo;
054: import org.netbeans.api.java.source.CompilationInfo;
055: import org.netbeans.api.java.source.ElementHandle;
056: import org.netbeans.api.java.source.JavaSource;
057: import org.netbeans.api.java.source.JavaSource.Phase;
058: import org.netbeans.api.java.source.ModificationResult;
059: import org.netbeans.api.java.source.SourceUtils;
060: import org.netbeans.api.java.source.TreeMaker;
061: import org.netbeans.api.java.source.TypeMirrorHandle;
062: import org.netbeans.api.java.source.WorkingCopy;
063: import org.netbeans.modules.java.editor.codegen.GeneratorUtils;
064: import org.netbeans.modules.java.hints.infrastructure.ErrorHintsProvider;
065: import org.netbeans.spi.editor.hints.ChangeInfo;
066: import org.netbeans.spi.editor.hints.Fix;
067: import org.openide.filesystems.FileObject;
068: import org.openide.util.NbBundle;
069:
070: /**
071: *
072: * @author Jan lahoda
073: */
074: public final class CreateMethodFix implements Fix {
075:
076: private FileObject targetFile;
077: private ElementHandle<TypeElement> target;
078: private TypeMirrorHandle returnType;
079: private List<TypeMirrorHandle> argumentTypes;
080: private List<String> argumentNames;
081: private ClasspathInfo cpInfo;
082: private Set<Modifier> modifiers;
083:
084: private String name;
085: private String inFQN;
086: private String methodDisplayName;
087:
088: public CreateMethodFix(CompilationInfo info, String name,
089: Set<Modifier> modifiers, TypeElement target,
090: TypeMirror returnType,
091: List<? extends TypeMirror> argumentTypes,
092: List<String> argumentNames, FileObject targetFile) {
093: this .name = name;
094: this .inFQN = target.getQualifiedName().toString();
095: this .cpInfo = info.getClasspathInfo();
096: this .modifiers = modifiers;
097: this .targetFile = targetFile;
098: this .target = ElementHandle.create(target);
099: if (returnType != null && returnType.getKind() == TypeKind.NULL) {
100: returnType = info.getElements().getTypeElement(
101: "java.lang.Object").asType(); // NOI18N
102: }
103: this .returnType = returnType != null ? TypeMirrorHandle
104: .create(returnType) : null;
105: this .argumentTypes = new ArrayList<TypeMirrorHandle>();
106:
107: for (TypeMirror tm : argumentTypes) {
108: this .argumentTypes.add(TypeMirrorHandle.create(tm));
109: }
110:
111: this .argumentNames = argumentNames;
112:
113: StringBuilder methodDisplayName = new StringBuilder();
114:
115: if (returnType != null) {
116: methodDisplayName.append(name);
117: } else {
118: methodDisplayName.append(target.getSimpleName().toString());
119: }
120:
121: methodDisplayName.append('(');
122:
123: boolean first = true;
124:
125: for (TypeMirror tm : argumentTypes) {
126: if (!first)
127: methodDisplayName.append(','); // NOI18N
128: first = false;
129: methodDisplayName
130: .append(org.netbeans.modules.editor.java.Utilities
131: .getTypeName(tm, true));
132: }
133:
134: methodDisplayName.append(')'); // NOI18N
135:
136: this .methodDisplayName = methodDisplayName.toString();
137: }
138:
139: public String getText() {
140: if (target.getKind() == ElementKind.ANNOTATION_TYPE)
141: return NbBundle.getMessage(CreateMethodFix.class,
142: "LBL_FIX_Create_Annotation_Element",
143: methodDisplayName, inFQN);
144: if (returnType != null) {
145: return NbBundle.getMessage(CreateMethodFix.class,
146: "LBL_FIX_Create_Method", methodDisplayName, inFQN);
147: } else {
148: return NbBundle.getMessage(CreateMethodFix.class,
149: "LBL_FIX_Create_Constructor", methodDisplayName,
150: inFQN);
151: }
152: }
153:
154: public ChangeInfo implement() throws IOException {
155: //use the original cp-info so it is "sure" that the proposedType can be resolved:
156: JavaSource js = JavaSource.create(cpInfo, targetFile);
157:
158: ModificationResult diff = js
159: .runModificationTask(new Task<WorkingCopy>() {
160: public void run(final WorkingCopy working)
161: throws IOException {
162: working.toPhase(Phase.RESOLVED);
163: TypeElement targetType = target
164: .resolve(working);
165:
166: if (targetType == null) {
167: ErrorHintsProvider.LOG.log(Level.INFO,
168: "Cannot resolve target."); // NOI18N
169: return;
170: }
171:
172: TreePath targetTree = working.getTrees()
173: .getPath(targetType);
174:
175: if (targetTree == null) {
176: ErrorHintsProvider.LOG.log(Level.INFO,
177: "Cannot resolve target tree: "
178: + targetType
179: .getQualifiedName()
180: + "."); // NOI18N
181: return;
182: }
183:
184: TypeMirrorHandle returnTypeHandle = CreateMethodFix.this .returnType;
185: TypeMirror returnType = returnTypeHandle != null ? returnTypeHandle
186: .resolve(working)
187: : null;
188:
189: if (returnTypeHandle != null
190: && returnType == null) {
191: ErrorHintsProvider.LOG.log(Level.INFO,
192: "Cannot resolve proposed type."); // NOI18N
193: return;
194: }
195:
196: TreeMaker make = working.getTreeMaker();
197:
198: List<VariableTree> argTypes = new ArrayList<VariableTree>();
199: Iterator<TypeMirrorHandle> typeIt = CreateMethodFix.this .argumentTypes
200: .iterator();
201: Iterator<String> nameIt = CreateMethodFix.this .argumentNames
202: .iterator();
203:
204: while (typeIt.hasNext() && nameIt.hasNext()) {
205: TypeMirrorHandle tmh = typeIt.next();
206: String argName = nameIt.next();
207:
208: argTypes.add(make.Variable(make
209: .Modifiers(EnumSet
210: .noneOf(Modifier.class)),
211: argName, make.Type(tmh
212: .resolve(working)), null));
213: }
214:
215: BlockTree body = targetType.getKind().isClass() ? createDefaultMethodBody(
216: working, returnType)
217: : null;
218: MethodTree mt = make
219: .Method(
220: make.Modifiers(modifiers),
221: name,
222: returnType != null ? make
223: .Type(returnType)
224: : null,
225: Collections
226: .<TypeParameterTree> emptyList(),
227: argTypes,
228: Collections
229: .<ExpressionTree> emptyList(),
230: body, null);
231: ClassTree decl = GeneratorUtils
232: .insertClassMember(working, targetTree,
233: mt);
234:
235: working.rewrite(targetTree.getLeaf(), decl);
236: }
237: });
238:
239: return Utilities.commitAndComputeChangeInfo(targetFile, diff);
240: }
241:
242: private void addArguments(CompilationInfo info, StringBuilder value) {
243: value.append("("); // NOI18N
244:
245: Iterator<TypeMirrorHandle> typeIt = CreateMethodFix.this .argumentTypes
246: .iterator();
247: Iterator<String> nameIt = CreateMethodFix.this .argumentNames
248: .iterator();
249: boolean first = true;
250:
251: while (typeIt.hasNext() && nameIt.hasNext()) {
252: if (!first) {
253: value.append(",");
254: }
255: first = false;
256:
257: TypeMirrorHandle tmh = typeIt.next();
258: String argName = nameIt.next();
259:
260: value.append(org.netbeans.modules.editor.java.Utilities
261: .getTypeName(tmh.resolve(info), true));
262: value.append(' '); // NOI18N
263: value.append(argName);
264: }
265:
266: value.append(")"); // NOI18N
267: }
268:
269: public String toDebugString(CompilationInfo info) {
270: StringBuilder value = new StringBuilder();
271:
272: if (returnType != null) {
273: value.append("CreateMethodFix:"); // NOI18N
274: value.append(name);
275: addArguments(info, value);
276: value.append(org.netbeans.modules.editor.java.Utilities
277: .getTypeName(returnType.resolve(info), true));
278: } else {
279: value.append("CreateConstructorFix:"); // NOI18N
280: addArguments(info, value);
281: }
282:
283: value.append(':'); // NOI18N
284: value.append(inFQN); // NOI18N
285:
286: return value.toString();
287: }
288:
289: //XXX should be moved into the GeneratorUtils:
290: private static BlockTree createDefaultMethodBody(WorkingCopy wc,
291: TypeMirror returnType) {
292: TreeMaker make = wc.getTreeMaker();
293: List<StatementTree> blockStatements = new ArrayList<StatementTree>();
294: TypeElement uoe = wc.getElements().getTypeElement(
295: "java.lang.UnsupportedOperationException"); // NOI18N
296: if (uoe != null) {
297: NewClassTree nue = make.NewClass(null, Collections
298: .<ExpressionTree> emptyList(), make.QualIdent(uoe),
299: Collections.singletonList(make
300: .Literal("Not yet implemented")), null);
301: blockStatements.add(make.Throw(nue));
302: }
303: return make.Block(blockStatements, false);
304: }
305:
306: }
|