001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.java.hints.errors;
042:
043: import com.sun.source.tree.BlockTree;
044: import com.sun.source.tree.ExpressionTree;
045: import com.sun.source.tree.MemberSelectTree;
046: import com.sun.source.tree.MethodInvocationTree;
047: import com.sun.source.tree.MethodTree;
048: import com.sun.source.tree.NewClassTree;
049: import com.sun.source.tree.ParameterizedTypeTree;
050: import com.sun.source.tree.Tree;
051: import com.sun.source.tree.Tree.Kind;
052: import com.sun.source.util.TreePath;
053: import java.io.IOException;
054: import java.util.ArrayList;
055: import java.util.Arrays;
056: import java.util.Collections;
057: import java.util.EnumSet;
058: import java.util.HashSet;
059: import java.util.LinkedList;
060: import java.util.List;
061: import java.util.Set;
062: import java.util.logging.Level;
063: import java.util.logging.Logger;
064: import javax.lang.model.element.Element;
065: import javax.lang.model.element.ElementKind;
066: import javax.lang.model.element.Modifier;
067: import javax.lang.model.element.ExecutableElement;
068: import javax.lang.model.element.PackageElement;
069: import javax.lang.model.element.TypeElement;
070: import javax.lang.model.type.ArrayType;
071: import javax.lang.model.type.DeclaredType;
072: import javax.lang.model.type.TypeKind;
073: import javax.lang.model.type.TypeMirror;
074: import org.netbeans.api.java.classpath.ClassPath;
075: import org.netbeans.api.java.source.ClasspathInfo.PathKind;
076: import org.netbeans.api.java.source.CompilationInfo;
077: import org.netbeans.api.java.source.ElementHandle;
078: import org.netbeans.api.java.source.SourceUtils;
079: import org.netbeans.modules.java.hints.errors.CreateClassFix.CreateInnerClassFix;
080: import org.netbeans.modules.java.hints.errors.CreateClassFix.CreateOuterClassFix;
081: import org.netbeans.modules.java.hints.infrastructure.ErrorHintsProvider;
082: import org.netbeans.modules.java.hints.infrastructure.Pair;
083: import org.netbeans.modules.java.hints.spi.ErrorRule;
084: import org.netbeans.spi.editor.hints.Fix;
085: import org.openide.ErrorManager;
086: import org.openide.filesystems.FileObject;
087: import org.openide.util.Exceptions;
088: import org.openide.util.NbBundle;
089:
090: import static org.netbeans.modules.java.hints.errors.CreateElementUtilities.*;
091:
092: /**
093: *
094: * @author Jan Lahoda
095: */
096: public final class CreateElement implements ErrorRule<Void> {
097:
098: /** Creates a new instance of CreateElement */
099: public CreateElement() {
100: }
101:
102: public Set<String> getCodes() {
103: return new HashSet<String>(Arrays.asList(
104: "compiler.err.cant.resolve.location",
105: "compiler.err.cant.apply.symbol",
106: "compiler.err.cant.resolve")); // NOI18N
107: }
108:
109: public List<Fix> run(CompilationInfo info, String diagnosticKey,
110: int offset, TreePath treePath, Data<Void> data) {
111: try {
112: return analyze(info, offset);
113: } catch (IOException e) {
114: Exceptions.printStackTrace(e);
115: return null;
116: } catch (ClassCastException e) {
117: Logger.getLogger(CreateElement.class.getName()).log(
118: Level.FINE, null, e);
119: return null;
120: }
121: }
122:
123: static List<Fix> analyze(CompilationInfo info, int offset)
124: throws IOException {
125: TreePath errorPath = ErrorHintsProvider.findUnresolvedElement(
126: info, offset);
127:
128: if (errorPath == null) {
129: return Collections.<Fix> emptyList();
130: }
131:
132: if (info.getElements().getTypeElement("java.lang.Object") == null) { // NOI18N
133: // broken java platform
134: return Collections.<Fix> emptyList();
135: }
136:
137: TreePath parent = null;
138: TreePath firstClass = null;
139: TreePath firstMethod = null;
140: TreePath firstInitializer = null;
141: TreePath methodInvocation = null;
142: TreePath newClass = null;
143: boolean lookupMethodInvocation = true;
144: boolean lookupNCT = true;
145:
146: TreePath path = info.getTreeUtilities().pathFor(offset + 1);
147:
148: while (path != null) {
149: Tree leaf = path.getLeaf();
150: Kind leafKind = leaf.getKind();
151:
152: if (parent != null
153: && parent.getLeaf() == errorPath.getLeaf())
154: parent = path;
155: if (leaf == errorPath.getLeaf() && parent == null)
156: parent = path;
157: if (leafKind == Kind.CLASS && firstClass == null)
158: firstClass = path;
159: if (leafKind == Kind.METHOD && firstMethod == null
160: && firstClass == null)
161: firstMethod = path;
162: //static/dynamic initializer:
163: if (leafKind == Kind.BLOCK
164: && path.getParentPath().getLeaf().getKind() == Kind.CLASS
165: && firstMethod == null && firstClass == null)
166: firstInitializer = path;
167:
168: if (lookupMethodInvocation
169: && leafKind == Kind.METHOD_INVOCATION) {
170: methodInvocation = path;
171: }
172:
173: if (lookupNCT && leafKind == Kind.NEW_CLASS) {
174: newClass = path;
175: }
176:
177: if (leafKind == Kind.MEMBER_SELECT) {
178: lookupMethodInvocation = leaf == errorPath.getLeaf();
179: }
180:
181: if (leafKind != Kind.MEMBER_SELECT
182: && leafKind != Kind.IDENTIFIER) {
183: lookupMethodInvocation = false;
184: }
185:
186: if (leafKind != Kind.MEMBER_SELECT
187: && leafKind != Kind.IDENTIFIER
188: && leafKind != Kind.PARAMETERIZED_TYPE) {
189: lookupNCT = false;
190: }
191:
192: path = path.getParentPath();
193: }
194:
195: if (parent == null || parent.getLeaf() == errorPath.getLeaf()
196: || firstClass == null)
197: return Collections.<Fix> emptyList();
198:
199: Element e = info.getTrees().getElement(errorPath);
200:
201: if (e == null) {
202: return Collections.<Fix> emptyList();
203: }
204:
205: Set<Modifier> modifiers = EnumSet.noneOf(Modifier.class);
206: String simpleName = e.getSimpleName().toString();
207: TypeElement source = (TypeElement) info.getTrees().getElement(
208: firstClass);
209: TypeElement target = null;
210: boolean wasMemberSelect = false;
211:
212: if (errorPath.getLeaf().getKind() == Kind.MEMBER_SELECT) {
213: TreePath exp = new TreePath(errorPath,
214: ((MemberSelectTree) errorPath.getLeaf())
215: .getExpression());
216: Element targetElement = info.getTrees().getElement(exp);
217: TypeMirror targetType = info.getTrees().getTypeMirror(exp);
218:
219: if (targetElement != null && targetType != null
220: && targetType.getKind() != TypeKind.ERROR) {
221: switch (targetElement.getKind()) {
222: case CLASS:
223: case INTERFACE:
224: case ENUM:
225: case ANNOTATION_TYPE:
226: //situation like <something>.ClassName.<identifier>,
227: //targetElement representing <something>.ClassName:
228: //the new element needs to be static
229: target = (TypeElement) targetElement;
230: modifiers.add(Modifier.STATIC);
231: break;
232:
233: case FIELD:
234: case ENUM_CONSTANT:
235: case LOCAL_VARIABLE:
236: case PARAMETER:
237: case EXCEPTION_PARAMETER:
238: TypeMirror tm = targetElement.asType();
239: if (tm.getKind() == TypeKind.DECLARED) {
240: target = (TypeElement) ((DeclaredType) tm)
241: .asElement();
242: }
243: break;
244: case METHOD:
245: Element el = info.getTypes().asElement(
246: ((ExecutableElement) targetElement)
247: .getReturnType());
248:
249: if (el != null
250: && (el.getKind().isClass() || el.getKind()
251: .isInterface())) {
252: target = (TypeElement) el;
253: }
254:
255: break;
256: case CONSTRUCTOR:
257: target = (TypeElement) targetElement
258: .getEnclosingElement();
259: break;
260: //TODO: type parameter?
261: }
262: }
263:
264: wasMemberSelect = true;
265: } else {
266: Element enclosingElement = e.getEnclosingElement();
267: if (enclosingElement != null
268: && enclosingElement.getKind() == ElementKind.ANNOTATION_TYPE) //unresolved element inside annot.
269: target = (TypeElement) enclosingElement;
270: else
271:
272: if (errorPath.getLeaf().getKind() == Kind.IDENTIFIER) {
273: //TODO: Handle Annotations
274: target = source;
275:
276: if (firstMethod != null) {
277: if (((MethodTree) firstMethod.getLeaf())
278: .getModifiers().getFlags().contains(
279: Modifier.STATIC)) {
280: modifiers.add(Modifier.STATIC);
281: }
282: } else {
283: if (firstInitializer != null) {
284: if (((BlockTree) firstInitializer.getLeaf())
285: .isStatic()) {
286: modifiers.add(Modifier.STATIC);
287: }
288: } else {
289: //TODO: otherwise.
290: }
291: }
292: }
293: }
294:
295: if (target == null) {
296: if (ErrorHintsProvider.ERR
297: .isLoggable(ErrorManager.INFORMATIONAL)) {
298: ErrorHintsProvider.ERR.log(ErrorManager.INFORMATIONAL,
299: "target=null"); // NOI18N
300: ErrorHintsProvider.ERR.log(ErrorManager.INFORMATIONAL,
301: "offset=" + offset); // NOI18N
302: ErrorHintsProvider.ERR.log(ErrorManager.INFORMATIONAL,
303: "errorTree=" + errorPath.getLeaf()); // NOI18N
304: }
305:
306: return Collections.<Fix> emptyList();
307: }
308:
309: modifiers.addAll(getAccessModifiers(info, source, target));
310:
311: List<Fix> result = new ArrayList<Fix>();
312:
313: if (methodInvocation != null) {
314: //create method:
315: MethodInvocationTree mit = (MethodInvocationTree) methodInvocation
316: .getLeaf();
317: //return type:
318: Set<ElementKind> fixTypes = EnumSet
319: .noneOf(ElementKind.class);
320: List<? extends TypeMirror> types = resolveType(fixTypes,
321: info, methodInvocation.getParentPath(),
322: methodInvocation.getLeaf(), offset, null, null);
323:
324: if (types == null || types.isEmpty()) {
325: return Collections.<Fix> emptyList();
326: }
327: result.addAll(prepareCreateMethodFix(info,
328: methodInvocation, modifiers, target, simpleName,
329: mit.getArguments(), types));
330: }
331:
332: if (newClass != null) {
333: //create method:
334: NewClassTree nct = (NewClassTree) newClass.getLeaf();
335: Element clazz = info.getTrees().getElement(
336: new TreePath(newClass, nct.getIdentifier()));
337:
338: if (clazz == null
339: || clazz.asType().getKind() == TypeKind.ERROR
340: || (!clazz.getKind().isClass() && !clazz.getKind()
341: .isInterface())) {
342: //the class does not exist...
343: ExpressionTree ident = nct.getIdentifier();
344: int numTypeArguments = 0;
345:
346: if (ident.getKind() == Kind.PARAMETERIZED_TYPE) {
347: numTypeArguments = ((ParameterizedTypeTree) ident)
348: .getTypeArguments().size();
349: }
350:
351: if (wasMemberSelect) {
352: return prepareCreateInnerClassFix(info, newClass,
353: target, modifiers, simpleName, nct
354: .getArguments(), null,
355: ElementKind.CLASS, numTypeArguments);
356: } else {
357: return prepareCreateOuterClassFix(info, newClass,
358: source, EnumSet.noneOf(Modifier.class),
359: simpleName, nct.getArguments(), null,
360: ElementKind.CLASS, numTypeArguments);
361: }
362: }
363:
364: if (nct.getClassBody() != null) {
365: return Collections.<Fix> emptyList();
366: }
367:
368: target = (TypeElement) clazz;
369:
370: result.addAll(prepareCreateMethodFix(info, newClass,
371: getAccessModifiers(info, source, target), target,
372: "<init>", nct.getArguments(), null));
373: }
374:
375: //field like or class (type):
376: Set<ElementKind> fixTypes = EnumSet.noneOf(ElementKind.class);
377: TypeMirror[] super Type = new TypeMirror[1];
378: int[] numTypeParameters = new int[1];
379: List<? extends TypeMirror> types = resolveType(fixTypes, info,
380: parent, errorPath.getLeaf(), offset, super Type,
381: numTypeParameters);
382: ElementKind classType = getClassType(fixTypes);
383:
384: if (classType != null) {
385: if (wasMemberSelect) {
386: result.addAll(prepareCreateInnerClassFix(info, null,
387: target, modifiers, simpleName, null,
388: super Type[0], classType, numTypeParameters[0]));
389: } else {
390: result.addAll(prepareCreateOuterClassFix(info, null,
391: source, EnumSet.noneOf(Modifier.class),
392: simpleName, null, super Type[0], classType,
393: numTypeParameters[0]));
394: }
395: }
396:
397: if (types == null || types.isEmpty()) {
398: return result;
399: }
400:
401: //XXX: should reasonably consider all the found type candidates, not only the one:
402: TypeMirror type = types.get(0);
403:
404: if (type == null || type.getKind() == TypeKind.VOID
405: || type.getKind() == TypeKind.EXECUTABLE) {
406: return result;
407: }
408:
409: //currently, we cannot handle error types, TYPEVARs and WILDCARDs:
410: if (containsErrorsOrTypevarsRecursively(type)) {
411: return result;
412: }
413:
414: if (fixTypes.contains(ElementKind.FIELD)
415: && isTargetWritable(target, info)) { //IZ 111048 -- don't offer anything if target file isn't writable
416: Element enclosingElement = e.getEnclosingElement();
417: if (enclosingElement != null
418: && enclosingElement.getKind() == ElementKind.ANNOTATION_TYPE) {
419: FileObject targetFile = SourceUtils.getFile(target,
420: info.getClasspathInfo());
421:
422: if (targetFile != null) {
423: result.add(new CreateMethodFix(info, simpleName,
424: modifiers, target, type, types, Collections
425: .<String> emptyList(), targetFile));
426: }
427:
428: return result;
429: } else {
430: FileObject targetFile = SourceUtils.getFile(target,
431: info.getClasspathInfo());
432:
433: if (targetFile != null) {
434: result.add(new CreateFieldFix(info, simpleName,
435: modifiers, target, type, targetFile));
436: }
437: }
438: }
439:
440: if (!wasMemberSelect
441: && (fixTypes.contains(ElementKind.LOCAL_VARIABLE) || types
442: .contains(ElementKind.PARAMETER))) {
443: ExecutableElement ee = null;
444:
445: if (firstMethod != null) {
446: ee = (ExecutableElement) info.getTrees().getElement(
447: firstMethod);
448: }
449:
450: if ((ee != null) && type != null) {
451: int identifierPos = (int) info.getTrees()
452: .getSourcePositions().getStartPosition(
453: info.getCompilationUnit(),
454: errorPath.getLeaf());
455: if (ee != null
456: && fixTypes.contains(ElementKind.PARAMETER)
457: && !Utilities.isMethodHeaderInsideGuardedBlock(
458: info, (MethodTree) firstMethod
459: .getLeaf()))
460: result.add(new AddParameterOrLocalFix(info, type,
461: simpleName, true, identifierPos));
462: if (fixTypes.contains(ElementKind.LOCAL_VARIABLE)
463: && ErrorFixesFakeHint
464: .enabled(ErrorFixesFakeHint.FixKind.CREATE_LOCAL_VARIABLE))
465: result.add(new AddParameterOrLocalFix(info, type,
466: simpleName, false, identifierPos));
467: }
468: }
469:
470: return result;
471: }
472:
473: private static List<Fix> prepareCreateMethodFix(
474: CompilationInfo info, TreePath invocation,
475: Set<Modifier> modifiers, TypeElement target,
476: String simpleName,
477: List<? extends ExpressionTree> arguments,
478: List<? extends TypeMirror> returnTypes) {
479: //create method:
480: Pair<List<? extends TypeMirror>, List<String>> formalArguments = resolveArguments(
481: info, invocation, arguments);
482:
483: //return type:
484: //XXX: should reasonably consider all the found type candidates, not only the one:
485: TypeMirror returnType = returnTypes != null ? returnTypes
486: .get(0) : null;
487:
488: //currently, we cannot handle error types, TYPEVARs and WILDCARDs:
489: if (formalArguments == null || returnType != null
490: && containsErrorsOrTypevarsRecursively(returnType)) {
491: return Collections.<Fix> emptyList();
492: }
493:
494: //IZ 111048 -- don't offer anything if target file isn't writable
495: if (!isTargetWritable(target, info))
496: return Collections.<Fix> emptyList();
497:
498: FileObject targetFile = SourceUtils.getFile(target, info
499: .getClasspathInfo());
500:
501: if (targetFile == null)
502: return Collections.<Fix> emptyList();
503:
504: return Collections.<Fix> singletonList(new CreateMethodFix(
505: info, simpleName, modifiers, target, returnType,
506: formalArguments.getA(), formalArguments.getB(),
507: targetFile));
508: }
509:
510: private static Pair<List<? extends TypeMirror>, List<String>> resolveArguments(
511: CompilationInfo info, TreePath invocation,
512: List<? extends ExpressionTree> realArguments) {
513: List<TypeMirror> argumentTypes = new LinkedList<TypeMirror>();
514: List<String> argumentNames = new LinkedList<String>();
515: Set<String> usedArgumentNames = new HashSet<String>();
516:
517: for (ExpressionTree arg : realArguments) {
518: TypeMirror tm = info.getTrees().getTypeMirror(
519: new TreePath(invocation, arg));
520:
521: if (tm == null || containsErrorsOrTypevarsRecursively(tm)) {
522: return null;
523: }
524:
525: if (tm.getKind() == TypeKind.NULL) {
526: tm = info.getElements().getTypeElement(
527: "java.lang.Object").asType(); // NOI18N
528: }
529:
530: argumentTypes.add(tm);
531:
532: String proposedName = org.netbeans.modules.java.hints.errors.Utilities
533: .getName(arg);
534:
535: if (proposedName == null) {
536: proposedName = org.netbeans.modules.java.hints.errors.Utilities
537: .getName(tm);
538: }
539:
540: if (proposedName == null) {
541: proposedName = "arg"; // NOI18N
542: }
543:
544: if (usedArgumentNames.contains(proposedName)) {
545: int num = 0;
546:
547: while (usedArgumentNames.contains(proposedName + num)) {
548: num++;
549: }
550:
551: proposedName = proposedName + num;
552: }
553:
554: usedArgumentNames.add(proposedName);
555:
556: argumentNames.add(proposedName);
557: }
558:
559: return new Pair<List<? extends TypeMirror>, List<String>>(
560: argumentTypes, argumentNames);
561: }
562:
563: private static List<Fix> prepareCreateOuterClassFix(
564: CompilationInfo info, TreePath invocation,
565: TypeElement source, Set<Modifier> modifiers,
566: String simpleName,
567: List<? extends ExpressionTree> realArguments,
568: TypeMirror super Type, ElementKind kind,
569: int numTypeParameters) {
570: Pair<List<? extends TypeMirror>, List<String>> formalArguments = invocation != null ? resolveArguments(
571: info, invocation, realArguments)
572: : new Pair<List<? extends TypeMirror>, List<String>>(
573: null, null);
574:
575: if (formalArguments == null) {
576: return Collections.<Fix> emptyList();
577: }
578:
579: ClassPath cp = info.getClasspathInfo().getClassPath(
580: PathKind.SOURCE);
581: FileObject root = cp.findOwnerRoot(info.getFileObject());
582: TypeElement outer = info.getElementUtilities()
583: .outermostTypeElement(source);
584: PackageElement packageElement = (PackageElement) outer
585: .getEnclosingElement();
586:
587: return Collections.<Fix> singletonList(new CreateOuterClassFix(
588: info, root, packageElement.getQualifiedName()
589: .toString(), simpleName, modifiers,
590: formalArguments.getA(), formalArguments.getB(),
591: super Type, kind, numTypeParameters));
592: }
593:
594: private static List<Fix> prepareCreateInnerClassFix(
595: CompilationInfo info, TreePath invocation,
596: TypeElement target, Set<Modifier> modifiers,
597: String simpleName,
598: List<? extends ExpressionTree> realArguments,
599: TypeMirror super Type, ElementKind kind,
600: int numTypeParameters) {
601: Pair<List<? extends TypeMirror>, List<String>> formalArguments = invocation != null ? resolveArguments(
602: info, invocation, realArguments)
603: : new Pair<List<? extends TypeMirror>, List<String>>(
604: null, null);
605:
606: if (formalArguments == null) {
607: return Collections.<Fix> emptyList();
608: }
609:
610: //IZ 111048 -- don't offer anything if target file isn't writable
611: if (!isTargetWritable(target, info))
612: return Collections.<Fix> emptyList();
613:
614: FileObject targetFile = SourceUtils.getFile(target, info
615: .getClasspathInfo());
616:
617: if (targetFile == null)
618: return Collections.<Fix> emptyList();
619:
620: return Collections.<Fix> singletonList(new CreateInnerClassFix(
621: info, simpleName, modifiers, target, formalArguments
622: .getA(), formalArguments.getB(), super Type,
623: kind, numTypeParameters, targetFile));
624: }
625:
626: private static ElementKind getClassType(Set<ElementKind> types) {
627: if (types.contains(ElementKind.CLASS))
628: return ElementKind.CLASS;
629: if (types.contains(ElementKind.ANNOTATION_TYPE))
630: return ElementKind.ANNOTATION_TYPE;
631: if (types.contains(ElementKind.INTERFACE))
632: return ElementKind.INTERFACE;
633: if (types.contains(ElementKind.ENUM))
634: return ElementKind.ENUM;
635:
636: return null;
637: }
638:
639: public void cancel() {
640: //XXX: not done yet
641: }
642:
643: public String getId() {
644: return CreateElement.class.getName();
645: }
646:
647: public String getDisplayName() {
648: return NbBundle.getMessage(CreateElement.class,
649: "LBL_Create_Field");
650: }
651:
652: public String getDescription() {
653: return NbBundle.getMessage(CreateElement.class,
654: "DSC_Create_Field");
655: }
656:
657: //XXX: currently we cannot fix:
658: //xxx = new ArrayList<Unknown>();
659: //=>
660: //ArrayList<Unknown> xxx;
661: //xxx = new ArrayList<Unknown>();
662: private static boolean containsErrorsOrTypevarsRecursively(
663: TypeMirror tm) {
664: switch (tm.getKind()) {
665: case WILDCARD:
666: case TYPEVAR:
667: case ERROR:
668: return true;
669: case DECLARED:
670: DeclaredType type = (DeclaredType) tm;
671:
672: for (TypeMirror t : type.getTypeArguments()) {
673: if (containsErrorsOrTypevarsRecursively(t))
674: return true;
675: }
676:
677: return false;
678: case ARRAY:
679: return containsErrorsOrTypevarsRecursively(((ArrayType) tm)
680: .getComponentType());
681: default:
682: return false;
683: }
684: }
685:
686: /**
687: * Detects if targets file is non-null and writable
688: * @return true if target's file is writable
689: */
690: private static boolean isTargetWritable(TypeElement target,
691: CompilationInfo info) {
692: FileObject fo = SourceUtils.getFile(ElementHandle.create(target
693: .getEnclosingElement()), info.getClasspathInfo());
694: if (fo != null && fo.canWrite())
695: return true;
696: else
697: return false;
698: }
699:
700: static EnumSet<Modifier> getAccessModifiers(CompilationInfo info,
701: TypeElement source, TypeElement target) {
702: if (target.getKind().isInterface()) {
703: return EnumSet.of(Modifier.PUBLIC);
704: }
705:
706: TypeElement outterMostSource = info.getElementUtilities()
707: .outermostTypeElement(source);
708: TypeElement outterMostTarget = info.getElementUtilities()
709: .outermostTypeElement(target);
710:
711: if (outterMostSource.equals(outterMostTarget)) {
712: return EnumSet.of(Modifier.PRIVATE);
713: }
714:
715: Element sourcePackage = outterMostSource.getEnclosingElement();
716: Element targetPackage = outterMostTarget.getEnclosingElement();
717:
718: if (sourcePackage.equals(targetPackage)) {
719: return EnumSet.noneOf(Modifier.class);
720: }
721:
722: //TODO: protected?
723: return EnumSet.of(Modifier.PUBLIC);
724: }
725:
726: }
|