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.ClassTree;
031: import com.sun.source.tree.ExpressionTree;
032: import com.sun.source.tree.MethodTree;
033: import com.sun.source.tree.ModifiersTree;
034: import com.sun.source.tree.Tree;
035: import com.sun.source.tree.Tree.Kind;
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.LinkedList;
045: import java.util.List;
046: import java.util.Set;
047: import java.util.logging.Level;
048: import javax.lang.model.element.ElementKind;
049: import javax.lang.model.element.Modifier;
050: import javax.lang.model.element.TypeElement;
051: import javax.lang.model.type.DeclaredType;
052: import javax.lang.model.type.TypeKind;
053: import javax.lang.model.type.TypeMirror;
054: import org.netbeans.api.java.source.Task;
055: import org.netbeans.api.java.source.ClasspathInfo;
056: import org.netbeans.api.java.source.CompilationInfo;
057: import org.netbeans.api.java.source.ElementHandle;
058: import org.netbeans.api.java.source.JavaSource;
059: import org.netbeans.api.java.source.JavaSource.Phase;
060: import org.netbeans.api.java.source.ModificationResult;
061: import org.netbeans.api.java.source.SourceUtils;
062: import org.netbeans.api.java.source.TreeMaker;
063: import org.netbeans.api.java.source.TypeMirrorHandle;
064: import org.netbeans.api.java.source.WorkingCopy;
065: import org.netbeans.modules.java.editor.codegen.GeneratorUtils;
066: import org.netbeans.modules.java.hints.infrastructure.ErrorHintsProvider;
067: import org.netbeans.spi.editor.hints.ChangeInfo;
068: import org.netbeans.spi.editor.hints.Fix;
069: import org.openide.filesystems.FileObject;
070: import org.openide.filesystems.FileUtil;
071: import org.openide.filesystems.Repository;
072: import org.openide.loaders.DataFolder;
073: import org.openide.loaders.DataObject;
074: import org.openide.util.NbBundle;
075:
076: /**
077: *
078: * @author Jan lahoda
079: */
080: public abstract class CreateClassFix implements Fix {
081:
082: protected Set<Modifier> modifiers;
083: private List<TypeMirrorHandle> argumentTypes; //if a specific constructor should be created
084: private List<String> argumentNames; //dtto.
085: private List<TypeMirrorHandle> super Types;
086: protected ElementKind kind;
087: private int numTypeParameters;
088:
089: public CreateClassFix(CompilationInfo info,
090: Set<Modifier> modifiers,
091: List<? extends TypeMirror> argumentTypes,
092: List<String> argumentNames, TypeMirror super Type,
093: ElementKind kind, int numTypeParameters) {
094: this .modifiers = modifiers;
095:
096: if (argumentTypes != null && argumentNames != null) {
097: this .argumentTypes = new ArrayList<TypeMirrorHandle>();
098:
099: for (TypeMirror tm : argumentTypes) {
100: this .argumentTypes.add(TypeMirrorHandle.create(tm));
101: }
102:
103: this .argumentNames = argumentNames;
104: }
105:
106: if (super Type != null) {
107: super Types = new LinkedList<TypeMirrorHandle>();
108:
109: if (super Type.getKind() == TypeKind.DECLARED
110: && ""
111: .equals(info
112: .getElementUtilities()
113: .getBinaryName(
114: (TypeElement) ((DeclaredType) super Type)
115: .asElement()))) {
116: for (TypeMirror tm : info.getTypes().directSupertypes(
117: super Type)) {
118: super Types.add(TypeMirrorHandle.create(tm));
119: }
120: } else {
121: super Types.add(TypeMirrorHandle.create(super Type));
122: }
123: }
124:
125: this .kind = kind;
126: this .numTypeParameters = numTypeParameters;
127: }
128:
129: protected ClassTree createConstructor(WorkingCopy working,
130: TreePath targetTreePath) {
131: TreeMaker make = working.getTreeMaker();
132: ClassTree targetTree = (ClassTree) targetTreePath.getLeaf();
133: boolean removeDefaultConstructor = (kind == ElementKind.INTERFACE)
134: || (kind == ElementKind.ANNOTATION_TYPE);
135:
136: if (argumentNames != null) {
137: List<VariableTree> argTypes = new ArrayList<VariableTree>();
138: Iterator<TypeMirrorHandle> typeIt = argumentTypes
139: .iterator();
140: Iterator<String> nameIt = argumentNames.iterator();
141:
142: while (typeIt.hasNext() && nameIt.hasNext()) {
143: TypeMirrorHandle tmh = typeIt.next();
144: String argName = nameIt.next();
145:
146: argTypes.add(make.Variable(make.Modifiers(EnumSet
147: .noneOf(Modifier.class)), argName, make
148: .Type(tmh.resolve(working)), null));
149: }
150:
151: MethodTree constr = make.Method(make.Modifiers(EnumSet
152: .of(Modifier.PUBLIC/*!!!*/)), "<init>", null,
153: Collections.<TypeParameterTree> emptyList(),
154: argTypes, Collections.<ExpressionTree> emptyList(),
155: "{}" /*XXX*/, null); // NOI18N
156:
157: targetTree = GeneratorUtils.insertClassMember(working,
158: targetTreePath, constr);
159:
160: removeDefaultConstructor = true;
161: }
162:
163: if (removeDefaultConstructor) {
164: //remove the original constructor:
165: for (Tree t : targetTree.getMembers()) {
166: if (t.getKind() == Kind.METHOD) {
167: MethodTree mt = (MethodTree) t;
168:
169: if ("<init>".equals(mt.getName().toString())
170: && mt.getParameters().size() == 0) { // NOI18N
171: targetTree = make.removeClassMember(targetTree,
172: mt);
173: break;
174: }
175: }
176: }
177: }
178:
179: Tree extendsClause = null;
180: List<Tree> implements Clause = Collections.<Tree> emptyList();
181:
182: if (super Types != null) {
183: DeclaredType extendsType = null;
184: List<DeclaredType> implements Types = new LinkedList<DeclaredType>();
185:
186: for (TypeMirrorHandle h : super Types) {
187: TypeMirror tm = h.resolve(working);
188:
189: if (tm == null) {
190: //XXX: log
191: continue;
192: }
193:
194: if (tm.getKind() != TypeKind.DECLARED) {
195: //XXX: log
196: continue;
197: }
198:
199: DeclaredType dt = (DeclaredType) tm;
200:
201: if (dt.asElement().getKind().isClass()) {
202: if (extendsType != null) {
203: //XXX: log
204: }
205:
206: extendsType = dt;
207: } else {
208: implements Types.add(dt);
209: }
210: }
211:
212: if (extendsType != null
213: && !"java.lang.Object"
214: .equals(((TypeElement) extendsType
215: .asElement()).getQualifiedName()
216: .toString())) { // NOI18N
217: extendsClause = make.Type(extendsType);
218: }
219:
220: if (!implements Types.isEmpty()) {
221: implements Clause = new LinkedList<Tree>();
222:
223: for (DeclaredType dt : implements Types) {
224: implements Clause.add(make.Type(dt));
225: }
226: }
227: }
228:
229: ModifiersTree nueModifiers = make.Modifiers(modifiers);
230: List<TypeParameterTree> typeParameters = new LinkedList<TypeParameterTree>();
231:
232: for (int cntr = 0; cntr < numTypeParameters; cntr++) {
233: typeParameters.add(make.TypeParameter(
234: numTypeParameters == 1 ? "T" : "T" + cntr,
235: Collections.<ExpressionTree> emptyList())); // NOI18N
236: }
237:
238: switch (kind) {
239: case CLASS:
240: return make.Class(nueModifiers, targetTree.getSimpleName(),
241: typeParameters, extendsClause, implements Clause,
242: targetTree.getMembers());
243: case INTERFACE:
244: return make.Interface(nueModifiers, targetTree
245: .getSimpleName(), typeParameters, implements Clause,
246: targetTree.getMembers());
247: case ANNOTATION_TYPE:
248: return make.AnnotationType(nueModifiers, targetTree
249: .getSimpleName(), targetTree.getMembers());
250: case ENUM:
251: return make.Enum(nueModifiers, targetTree.getSimpleName(),
252: implements Clause, targetTree.getMembers());
253: default:
254: assert false : kind;
255: return null;
256: }
257: }
258:
259: private static int valueForBundle(ElementKind kind) {
260: switch (kind) {
261: case CLASS:
262: return 0;
263: case INTERFACE:
264: return 1;
265: case ENUM:
266: return 2;
267: case ANNOTATION_TYPE:
268: return 3;
269: default:
270: assert false : kind;
271: return 0;
272: }
273: }
274:
275: public abstract String toDebugString(CompilationInfo info);
276:
277: static final class CreateOuterClassFix extends CreateClassFix {
278: private FileObject targetSourceRoot;
279: private String packageName;
280: private String simpleName;
281:
282: public CreateOuterClassFix(CompilationInfo info,
283: FileObject targetSourceRoot, String packageName,
284: String simpleName, Set<Modifier> modifiers,
285: List<? extends TypeMirror> argumentTypes,
286: List<String> argumentNames, TypeMirror super Type,
287: ElementKind kind, int numTypeParameters) {
288: super (info, modifiers, argumentTypes, argumentNames,
289: super Type, kind, numTypeParameters);
290:
291: this .targetSourceRoot = targetSourceRoot;
292: this .packageName = packageName;
293: this .simpleName = simpleName;
294: }
295:
296: public String getText() {
297: return NbBundle.getMessage(CreateClassFix.class,
298: "FIX_CreateClassInPackage", simpleName,
299: packageName, valueForBundle(kind));
300: }
301:
302: private static String template(ElementKind kind) {
303: switch (kind) {
304: case CLASS:
305: return "Templates/Classes/Class.java"; // NOI18N
306: case INTERFACE:
307: return "Templates/Classes/Interface.java"; // NOI18N
308: case ANNOTATION_TYPE:
309: return "Templates/Classes/AnnotationType.java"; // NOI18N
310: case ENUM:
311: return "Templates/Classes/Enum.java"; // NOI18N
312: default:
313: throw new IllegalStateException();
314: }
315: }
316:
317: public ChangeInfo implement() throws IOException {
318: FileObject pack = FileUtil.createFolder(targetSourceRoot,
319: packageName.replace('.', '/')); // NOI18N
320: FileObject classTemplate/*???*/= Repository.getDefault()
321: .getDefaultFileSystem().getRoot().getFileObject(
322: template(kind));
323: DataObject classTemplateDO = DataObject.find(classTemplate);
324: DataObject od = classTemplateDO.createFromTemplate(
325: DataFolder.findFolder(pack), simpleName);
326: FileObject target = od.getPrimaryFile();
327:
328: JavaSource.forFileObject(target).runModificationTask(
329: new Task<WorkingCopy>() {
330: public void run(WorkingCopy parameter)
331: throws Exception {
332: parameter.toPhase(Phase.RESOLVED);
333:
334: ClassTree source = (ClassTree) parameter
335: .getCompilationUnit()
336: .getTypeDecls().get(0);
337: ClassTree nue = createConstructor(
338: parameter,
339: TreePath.getPath(parameter
340: .getCompilationUnit(),
341: source));
342:
343: parameter.rewrite(source, nue);
344: }
345: }).commit();
346:
347: return new ChangeInfo(target, null, null);
348: }
349:
350: public String toDebugString(CompilationInfo info) {
351: return "CreateClass:" + packageName + "." + simpleName
352: + ":" + modifiers.toString() + ":" + kind; // NOI18N
353: }
354:
355: }
356:
357: static final class CreateInnerClassFix extends CreateClassFix {
358:
359: private FileObject targetFile;
360: private ElementHandle<TypeElement> target;
361: private ClasspathInfo cpInfo;
362: private String name;
363: private String inFQN;
364:
365: public CreateInnerClassFix(CompilationInfo info, String name,
366: Set<Modifier> modifiers, TypeElement target,
367: List<? extends TypeMirror> argumentTypes,
368: List<String> argumentNames, TypeMirror super Type,
369: ElementKind kind, int numTypeParameters,
370: FileObject targetFile) {
371: super (info, modifiers, argumentTypes, argumentNames,
372: super Type, kind, numTypeParameters);
373: this .name = name;
374: this .target = ElementHandle.create(target);
375: this .inFQN = target.getQualifiedName().toString();
376: this .cpInfo = info.getClasspathInfo();
377: this .targetFile = targetFile;
378: }
379:
380: public String getText() {
381: return NbBundle.getMessage(CreateClassFix.class,
382: "FIX_CreateInnerClass", name, inFQN,
383: valueForBundle(kind));
384: }
385:
386: public ChangeInfo implement() throws Exception {
387: //use the original cp-info so it is "sure" that the target can be resolved:
388: JavaSource js = JavaSource.create(cpInfo, targetFile);
389:
390: ModificationResult diff = js
391: .runModificationTask(new Task<WorkingCopy>() {
392:
393: public void run(final WorkingCopy working)
394: throws IOException {
395: working.toPhase(Phase.RESOLVED);
396: TypeElement targetType = target
397: .resolve(working);
398:
399: if (targetType == null) {
400: ErrorHintsProvider.LOG.log(Level.INFO,
401: "Cannot resolve target."); // NOI18N
402: return;
403: }
404:
405: TreePath targetTree = working.getTrees()
406: .getPath(targetType);
407:
408: if (targetTree == null) {
409: ErrorHintsProvider.LOG
410: .log(
411: Level.INFO,
412: "Cannot resolve target tree: "
413: + targetType
414: .getQualifiedName()
415: + "."); // NOI18N
416: return;
417: }
418:
419: TreeMaker make = working.getTreeMaker();
420: MethodTree constr = make
421: .Method(
422: make
423: .Modifiers(EnumSet
424: .of(Modifier.PUBLIC)),
425: "<init>",
426: null,
427: Collections
428: .<TypeParameterTree> emptyList(),
429: Collections
430: .<VariableTree> emptyList(),
431: Collections
432: .<ExpressionTree> emptyList(),
433: "{}" /*XXX*/, null); // NOI18N
434: ClassTree innerClass = make
435: .Class(
436: make.Modifiers(modifiers),
437: name,
438: Collections
439: .<TypeParameterTree> emptyList(),
440: null,
441: Collections
442: .<Tree> emptyList(),
443: Collections
444: .<Tree> singletonList(constr));
445:
446: innerClass = createConstructor(
447: working,
448: new TreePath(targetTree, innerClass));
449:
450: working.rewrite(targetTree.getLeaf(),
451: GeneratorUtils.insertClassMember(
452: working, targetTree,
453: innerClass));
454: }
455: });
456:
457: return Utilities.commitAndComputeChangeInfo(targetFile,
458: diff);
459: }
460:
461: public String toDebugString(CompilationInfo info) {
462: return "CreateInnerClass:" + inFQN + "." + name + ":"
463: + modifiers.toString() + ":" + kind; // NOI18N
464: }
465:
466: }
467:
468: }
|