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:
0042: package org.netbeans.modules.refactoring.java;
0043:
0044: import com.sun.source.tree.ClassTree;
0045: import com.sun.source.tree.CompilationUnitTree;
0046: import com.sun.source.tree.ExpressionTree;
0047: import com.sun.source.tree.Tree;
0048: import com.sun.source.util.TreePath;
0049: import java.awt.Color;
0050: import java.awt.Dialog;
0051: import java.awt.event.ActionEvent;
0052: import java.awt.event.ActionListener;
0053: import java.io.File;
0054: import java.io.IOException;
0055: import java.io.UnsupportedEncodingException;
0056: import java.net.URISyntaxException;
0057: import java.net.URL;
0058: import java.net.URLDecoder;
0059: import java.text.MessageFormat;
0060: import java.util.ArrayList;
0061: import java.util.Arrays;
0062: import java.util.Collection;
0063: import java.util.Collections;
0064: import java.util.EnumSet;
0065: import java.util.HashSet;
0066: import java.util.LinkedList;
0067: import java.util.List;
0068: import java.util.Set;
0069: import java.util.StringTokenizer;
0070: import java.util.logging.Level;
0071: import java.util.logging.Logger;
0072: import javax.lang.model.element.Element;
0073: import javax.lang.model.element.ElementKind;
0074: import javax.lang.model.element.ExecutableElement;
0075: import javax.lang.model.element.TypeElement;
0076: import javax.lang.model.type.DeclaredType;
0077: import javax.lang.model.type.TypeKind;
0078: import javax.lang.model.type.TypeMirror;
0079: import javax.lang.model.type.TypeVariable;
0080: import javax.lang.model.type.WildcardType;
0081: import javax.lang.model.util.ElementFilter;
0082: import javax.lang.model.util.Types;
0083: import javax.swing.JLabel;
0084: import javax.swing.SwingConstants;
0085: import javax.swing.SwingUtilities;
0086: import javax.swing.border.EmptyBorder;
0087: import javax.swing.text.AttributeSet;
0088: import javax.swing.text.StyleConstants;
0089: import org.netbeans.api.editor.mimelookup.MimeLookup;
0090: import org.netbeans.api.editor.mimelookup.MimePath;
0091: import org.netbeans.api.editor.settings.FontColorSettings;
0092: import org.netbeans.api.java.classpath.ClassPath;
0093: import org.netbeans.api.java.classpath.ClassPath.Entry;
0094: import org.netbeans.api.java.classpath.GlobalPathRegistry;
0095: import org.netbeans.api.java.lexer.JavaTokenId;
0096: import org.netbeans.api.java.project.JavaProjectConstants;
0097: import org.netbeans.api.java.queries.SourceForBinaryQuery;
0098: import org.netbeans.api.java.queries.SourceForBinaryQuery.Result;
0099: import org.netbeans.api.java.source.CancellableTask;
0100: import org.netbeans.api.java.source.ClassIndex;
0101: import org.netbeans.api.java.source.CompilationInfo;
0102: import org.netbeans.api.java.source.ClasspathInfo;
0103: import org.netbeans.api.java.source.Comment;
0104: import org.netbeans.api.java.source.CompilationController;
0105: import org.netbeans.api.java.source.ElementHandle;
0106: import org.netbeans.api.java.source.JavaSource;
0107: import org.netbeans.api.java.source.SourceUtils;
0108: import org.netbeans.api.java.source.TreeMaker;
0109: import org.netbeans.api.java.source.TreePathHandle;
0110: import org.netbeans.api.java.source.TreeUtilities;
0111: import org.netbeans.api.java.source.WorkingCopy;
0112: import org.netbeans.api.lexer.Token;
0113: import org.netbeans.api.lexer.TokenHierarchy;
0114: import org.netbeans.api.lexer.TokenSequence;
0115: import org.netbeans.api.project.FileOwnerQuery;
0116: import org.netbeans.api.project.Project;
0117: import org.netbeans.api.project.ProjectUtils;
0118: import org.netbeans.api.project.SourceGroup;
0119: import org.netbeans.api.project.ui.OpenProjects;
0120: import org.netbeans.spi.java.classpath.support.ClassPathSupport;
0121: import org.openide.DialogDescriptor;
0122: import org.openide.DialogDisplayer;
0123: import org.openide.filesystems.FileObject;
0124: import org.openide.filesystems.FileUtil;
0125: import org.openide.util.Exceptions;
0126: import org.openide.util.Lookup;
0127: import org.openide.filesystems.FileUtil;
0128: import org.openide.filesystems.URLMapper;
0129: import org.openide.util.NbBundle;
0130: import org.openide.util.RequestProcessor;
0131: import org.openide.util.Utilities;
0132:
0133: /**
0134: *
0135: * @author Jan Becicka
0136: */
0137: public class RetoucheUtils {
0138:
0139: private static final String JAVA_MIME_TYPE = "text/x-java"; // NOI18N
0140: public static volatile boolean cancel = false;
0141: private static final Logger LOG = Logger
0142: .getLogger(RetoucheUtils.class.getName());
0143:
0144: public static String htmlize(String input) {
0145: String temp = org.openide.util.Utilities.replaceString(input,
0146: "<", "<"); // NOI18N
0147: temp = org.openide.util.Utilities.replaceString(temp, ">",
0148: ">"); // NOI18N
0149: return temp;
0150: }
0151:
0152: public static Collection<ExecutableElement> getOverridenMethods(
0153: ExecutableElement e, CompilationInfo info) {
0154: return getOverridenMethods(e, SourceUtils
0155: .getEnclosingTypeElement(e), info);
0156: }
0157:
0158: private static Collection<ExecutableElement> getOverridenMethods(
0159: ExecutableElement e, TypeElement parent,
0160: CompilationInfo info) {
0161: ArrayList<ExecutableElement> result = new ArrayList<ExecutableElement>();
0162:
0163: TypeMirror sup = parent.getSuperclass();
0164: if (sup.getKind() == TypeKind.DECLARED) {
0165: TypeElement next = (TypeElement) ((DeclaredType) sup)
0166: .asElement();
0167: ExecutableElement overriden = getMethod(e, next, info);
0168: result.addAll(getOverridenMethods(e, next, info));
0169: if (overriden != null) {
0170: result.add(overriden);
0171: }
0172: }
0173: for (TypeMirror tm : parent.getInterfaces()) {
0174: TypeElement next = (TypeElement) ((DeclaredType) tm)
0175: .asElement();
0176: ExecutableElement overriden2 = getMethod(e, next, info);
0177: result.addAll(getOverridenMethods(e, next, info));
0178: if (overriden2 != null) {
0179: result.add(overriden2);
0180: }
0181: }
0182: return result;
0183: }
0184:
0185: private static ExecutableElement getMethod(
0186: ExecutableElement method, TypeElement type,
0187: CompilationInfo info) {
0188: for (ExecutableElement met : ElementFilter.methodsIn(type
0189: .getEnclosedElements())) {
0190: if (info.getElements().overrides(method, met, type)) {
0191: return met;
0192: }
0193: }
0194: return null;
0195: }
0196:
0197: public static Set<ElementHandle<TypeElement>> getImplementorsAsHandles(
0198: ClassIndex idx, ClasspathInfo cpInfo, TypeElement el) {
0199: cancel = false;
0200: ClassPath source = cpInfo
0201: .getClassPath(ClasspathInfo.PathKind.SOURCE);
0202: LinkedList<ElementHandle<TypeElement>> elements = new LinkedList<ElementHandle<TypeElement>>(
0203: idx.getElements(ElementHandle.create(el), EnumSet
0204: .of(ClassIndex.SearchKind.IMPLEMENTORS),
0205: EnumSet.of(ClassIndex.SearchScope.SOURCE,
0206: ClassIndex.SearchScope.DEPENDENCIES)));
0207: HashSet<ElementHandle<TypeElement>> result = new HashSet<ElementHandle<TypeElement>>();
0208: while (!elements.isEmpty()) {
0209: if (cancel) {
0210: cancel = false;
0211: return Collections
0212: .<ElementHandle<TypeElement>> emptySet();
0213: }
0214: ElementHandle<TypeElement> next = elements.removeFirst();
0215: FileObject file = SourceUtils.getFile(next, cpInfo);
0216: if (file != null && source.contains(file)) {
0217: result.add(next);
0218: }
0219: elements.addAll(idx.getElements(next, EnumSet
0220: .of(ClassIndex.SearchKind.IMPLEMENTORS), EnumSet
0221: .of(ClassIndex.SearchScope.SOURCE,
0222: ClassIndex.SearchScope.DEPENDENCIES)));
0223: }
0224: return result;
0225: }
0226:
0227: public static Collection<ExecutableElement> getOverridingMethods(
0228: ExecutableElement e, CompilationInfo info) {
0229: Collection<ExecutableElement> result = new ArrayList();
0230: TypeElement parentType = (TypeElement) e.getEnclosingElement();
0231: //XXX: Fixme IMPLEMENTORS_RECURSIVE were removed
0232: Set<ElementHandle<TypeElement>> subTypes = getImplementorsAsHandles(
0233: info.getClasspathInfo().getClassIndex(), info
0234: .getClasspathInfo(), parentType);
0235: for (ElementHandle<TypeElement> subTypeHandle : subTypes) {
0236: TypeElement type = subTypeHandle.resolve(info);
0237: if (type == null && LOG.isLoggable(Level.INFO)) {
0238: // #120577: log info to find out what is going wrong
0239: FileObject file = SourceUtils.getFile(subTypeHandle,
0240: info.getClasspathInfo());
0241: LOG.log(Level.INFO, "#120577: Cannot resolve "
0242: + subTypeHandle + "; file: " + file); // NOI18N
0243: }
0244: for (ExecutableElement method : ElementFilter
0245: .methodsIn(type.getEnclosedElements())) {
0246: if (info.getElements().overrides(method, e, type)) {
0247: result.add(method);
0248: }
0249: }
0250: }
0251: return result;
0252: }
0253:
0254: public static boolean isJavaFile(FileObject f) {
0255: return JAVA_MIME_TYPE.equals(f.getMIMEType()); //NOI18N
0256: }
0257:
0258: public static String getHtml(String text) {
0259: StringBuffer buf = new StringBuffer();
0260: TokenHierarchy tokenH = TokenHierarchy.create(text, JavaTokenId
0261: .language());
0262: Lookup lookup = MimeLookup.getLookup(MimePath
0263: .get(JAVA_MIME_TYPE));
0264: FontColorSettings settings = lookup
0265: .lookup(FontColorSettings.class);
0266: TokenSequence tok = tokenH.tokenSequence();
0267: while (tok.moveNext()) {
0268: Token<JavaTokenId> token = (Token) tok.token();
0269: String category = token.id().primaryCategory();
0270: if (category == null) {
0271: category = "whitespace"; //NOI18N
0272: }
0273: AttributeSet set = settings.getTokenFontColors(category);
0274: buf.append(color(htmlize(token.text().toString()), set));
0275: }
0276: return buf.toString();
0277: }
0278:
0279: private static String color(String string, AttributeSet set) {
0280: if (set == null)
0281: return string;
0282: if (string.trim().length() == 0) {
0283: return Utilities.replaceString(Utilities.replaceString(
0284: string, " ", " "), "\n", "<br>"); //NOI18N
0285: }
0286: StringBuffer buf = new StringBuffer(string);
0287: if (StyleConstants.isBold(set)) {
0288: buf.insert(0, "<b>"); //NOI18N
0289: buf.append("</b>"); //NOI18N
0290: }
0291: if (StyleConstants.isItalic(set)) {
0292: buf.insert(0, "<i>"); //NOI18N
0293: buf.append("</i>"); //NOI18N
0294: }
0295: if (StyleConstants.isStrikeThrough(set)) {
0296: buf.insert(0, "<s>"); // NOI18N
0297: buf.append("</s>"); // NOI18N
0298: }
0299: buf
0300: .insert(0, "<font color="
0301: + getHTMLColor(StyleConstants
0302: .getForeground(set)) + ">"); //NOI18N
0303: buf.append("</font>"); //NOI18N
0304: return buf.toString();
0305: }
0306:
0307: private static String getHTMLColor(Color c) {
0308: String colorR = "0" + Integer.toHexString(c.getRed()); //NOI18N
0309: colorR = colorR.substring(colorR.length() - 2);
0310: String colorG = "0" + Integer.toHexString(c.getGreen()); //NOI18N
0311: colorG = colorG.substring(colorG.length() - 2);
0312: String colorB = "0" + Integer.toHexString(c.getBlue()); //NOI18N
0313: colorB = colorB.substring(colorB.length() - 2);
0314: String html_color = "#" + colorR + colorG + colorB; //NOI18N
0315: return html_color;
0316: }
0317:
0318: public static boolean isElementInOpenProject(FileObject f) {
0319: if (f == null)
0320: return false;
0321: Project p = FileOwnerQuery.getOwner(f);
0322: Project[] opened = OpenProjects.getDefault().getOpenProjects();
0323: for (int i = 0; i < opened.length; i++) {
0324: if (p == opened[i])
0325: return true;
0326: }
0327: return false;
0328: }
0329:
0330: public static boolean isFromLibrary(Element element,
0331: ClasspathInfo info) {
0332: FileObject file = SourceUtils.getFile(element, info);
0333: if (file == null) {
0334: //no source for given element. Element is from library
0335: return true;
0336: }
0337: return FileUtil.getArchiveFile(file) != null;
0338: }
0339:
0340: public static boolean isValidPackageName(String name) {
0341: if (name.endsWith(".")) //NOI18N
0342: return false;
0343: if (name.startsWith(".")) //NOI18N
0344: return false;
0345: StringTokenizer tokenizer = new StringTokenizer(name, "."); // NOI18N
0346: while (tokenizer.hasMoreTokens()) {
0347: if (!Utilities.isJavaIdentifier(tokenizer.nextToken())) {
0348: return false;
0349: }
0350: }
0351: return true;
0352: }
0353:
0354: public static boolean isFileInOpenProject(FileObject file) {
0355: assert file != null;
0356: Project p = FileOwnerQuery.getOwner(file);
0357: Project[] opened = OpenProjects.getDefault().getOpenProjects();
0358: for (int i = 0; i < opened.length; i++) {
0359: if (p == opened[i])
0360: return true;
0361: }
0362: return false;
0363: }
0364:
0365: public static boolean isOnSourceClasspath(FileObject fo) {
0366: Project p = FileOwnerQuery.getOwner(fo);
0367: if (p == null)
0368: return false;
0369: Project[] opened = OpenProjects.getDefault().getOpenProjects();
0370: if (!Arrays.asList(opened).contains(p)) {
0371: return false;
0372: }
0373: return ClassPath.getClassPath(fo, ClassPath.SOURCE) != null;
0374: }
0375:
0376: public static boolean isClasspathRoot(FileObject fo) {
0377: return fo.equals(ClassPath.getClassPath(fo, ClassPath.SOURCE)
0378: .findOwnerRoot(fo));
0379: }
0380:
0381: public static boolean isRefactorable(FileObject file) {
0382: return isJavaFile(file) && isFileInOpenProject(file)
0383: && isOnSourceClasspath(file);
0384: }
0385:
0386: public static String getPackageName(FileObject folder) {
0387: assert folder.isFolder() : "argument must be folder";
0388: return ClassPath.getClassPath(folder, ClassPath.SOURCE)
0389: .getResourceName(folder, '.', false);
0390: }
0391:
0392: public static String getPackageName(CompilationUnitTree unit) {
0393: assert unit != null;
0394: ExpressionTree name = unit.getPackageName();
0395: if (name == null) {
0396: //default package
0397: return "";
0398: }
0399: return name.toString();
0400: }
0401:
0402: public static String getPackageName(URL url) {
0403: File f = null;
0404: try {
0405: String path = URLDecoder.decode(url.getPath(), "utf-8"); // NOI18N
0406: f = FileUtil.normalizeFile(new File(path));
0407: } catch (UnsupportedEncodingException u) {
0408: throw new IllegalArgumentException(
0409: "Cannot create package name for url " + url); // NOI18N
0410: }
0411: String suffix = "";
0412:
0413: do {
0414: FileObject fo = FileUtil.toFileObject(f);
0415: if (fo != null) {
0416: if ("".equals(suffix))
0417: return getPackageName(fo);
0418: String prefix = getPackageName(fo);
0419: return prefix + ("".equals(prefix) ? "" : ".") + suffix; // NOI18N
0420: }
0421: if (!"".equals(suffix)) {
0422: suffix = "." + suffix; // NOI18N
0423: }
0424: try {
0425: suffix = URLDecoder.decode(f.getPath()
0426: .substring(
0427: f.getPath().lastIndexOf(
0428: File.separatorChar) + 1),
0429: "utf-8")
0430: + suffix; // NOI18N
0431: } catch (UnsupportedEncodingException u) {
0432: throw new IllegalArgumentException(
0433: "Cannot create package name for url " + url); // NOI18N
0434: }
0435: f = f.getParentFile();
0436: } while (f != null);
0437: throw new IllegalArgumentException(
0438: "Cannot create package name for url " + url); // NOI18N
0439: }
0440:
0441: /**
0442: * creates or finds FileObject according to
0443: * @param url
0444: * @return FileObject
0445: */
0446: public static FileObject getOrCreateFolder(URL url)
0447: throws IOException {
0448: try {
0449: FileObject result = URLMapper.findFileObject(url);
0450: if (result != null)
0451: return result;
0452: File f = new File(url.toURI());
0453:
0454: result = FileUtil.createFolder(f);
0455: return result;
0456: } catch (URISyntaxException ex) {
0457: throw (IOException) new IOException().initCause(ex);
0458: }
0459: }
0460:
0461: public static FileObject getClassPathRoot(URL url)
0462: throws IOException {
0463: FileObject result = URLMapper.findFileObject(url);
0464: File f = FileUtil.normalizeFile(new File(URLDecoder.decode(url
0465: .getPath())));
0466: while (result == null) {
0467: result = FileUtil.toFileObject(f);
0468: f = f.getParentFile();
0469: }
0470: return ClassPath.getClassPath(result, ClassPath.SOURCE)
0471: .findOwnerRoot(result);
0472: }
0473:
0474: public static Collection<TypeElement> getSuperTypes(
0475: TypeElement type, CompilationInfo info) {
0476: Collection<TypeElement> result = new HashSet<TypeElement>();
0477: LinkedList<TypeElement> l = new LinkedList<TypeElement>();
0478: l.add(type);
0479: while (!l.isEmpty()) {
0480: TypeElement t = l.removeFirst();
0481: TypeElement super Class = typeToElement(t.getSuperclass(),
0482: info);
0483: if (super Class != null) {
0484: result.add(super Class);
0485: l.addLast((TypeElement) super Class);
0486: }
0487: Collection<TypeElement> interfaces = typesToElements(t
0488: .getInterfaces(), info);
0489: result.addAll(interfaces);
0490: l.addAll(interfaces);
0491: }
0492: return result;
0493: }
0494:
0495: public static Collection<FileObject> getSuperTypesFiles(
0496: TreePathHandle handle) {
0497: try {
0498: SuperTypesTask ff;
0499: JavaSource source = JavaSource.forFileObject(handle
0500: .getFileObject());
0501: source.runUserActionTask(ff = new SuperTypesTask(handle),
0502: true);
0503: return ff.getFileObjects();
0504: } catch (IOException ex) {
0505: throw (RuntimeException) new RuntimeException()
0506: .initCause(ex);
0507: }
0508: }
0509:
0510: public static Collection<TypeElement> getSuperTypes(
0511: TypeElement type, CompilationInfo info, boolean sourceOnly) {
0512: if (!sourceOnly)
0513: return getSuperTypes(type, info);
0514: Collection<TypeElement> result = new HashSet<TypeElement>();
0515: for (TypeElement el : getSuperTypes(type, info)) {
0516: FileObject file = SourceUtils.getFile(el, info
0517: .getClasspathInfo());
0518: if (file != null && isFileInOpenProject(file)
0519: && !isFromLibrary(el, info.getClasspathInfo())) {
0520: result.add(el);
0521: }
0522: }
0523: return result;
0524: }
0525:
0526: public static TypeElement typeToElement(TypeMirror type,
0527: CompilationInfo info) {
0528: return (TypeElement) info.getTypes().asElement(type);
0529: }
0530:
0531: private static Collection<TypeElement> typesToElements(
0532: Collection<? extends TypeMirror> types, CompilationInfo info) {
0533: Collection<TypeElement> result = new HashSet();
0534: for (TypeMirror tm : types) {
0535: result.add(typeToElement(tm, info));
0536: }
0537: return result;
0538: }
0539:
0540: public static Collection<FileObject> elementsToFile(
0541: Collection<? extends Element> elements, ClasspathInfo cpInfo) {
0542: Collection<FileObject> result = new HashSet();
0543: for (Element handle : elements) {
0544: result.add(SourceUtils.getFile(handle, cpInfo));
0545: }
0546: return result;
0547: }
0548:
0549: public static boolean elementExistsIn(TypeElement target,
0550: Element member, CompilationInfo info) {
0551: for (Element currentMember : target.getEnclosedElements()) {
0552: if (info.getElements().hides(member, currentMember)
0553: || info.getElements().hides(currentMember, member))
0554: return true;
0555: if (member instanceof ExecutableElement
0556: && currentMember instanceof ExecutableElement
0557: && (info.getElements().overrides(
0558: (ExecutableElement) member,
0559: (ExecutableElement) currentMember, target) || (info
0560: .getElements().overrides(
0561: (ExecutableElement) currentMember,
0562: (ExecutableElement) member, target)))) {
0563: return true;
0564: }
0565: }
0566: return false;
0567: }
0568:
0569: public static ElementHandle getElementHandle(TreePathHandle tph) {
0570: try {
0571: CompilerTask ff;
0572: JavaSource source = JavaSource.forFileObject(tph
0573: .getFileObject());
0574: source.runUserActionTask(ff = new CompilerTask(tph), true);
0575: return ff.getElementHandle();
0576: } catch (IOException ex) {
0577: throw (RuntimeException) new RuntimeException()
0578: .initCause(ex);
0579: }
0580: }
0581:
0582: public static ElementKind getElementKind(TreePathHandle tph) {
0583: try {
0584: CompilerTask ff;
0585: JavaSource source = JavaSource.forFileObject(tph
0586: .getFileObject());
0587: source.runUserActionTask(ff = new CompilerTask(tph), true);
0588: return ff.getElementKind();
0589: } catch (IOException ex) {
0590: throw (RuntimeException) new RuntimeException()
0591: .initCause(ex);
0592: }
0593: }
0594:
0595: public static String getSimpleName(TreePathHandle tph) {
0596: try {
0597: CompilerTask ff;
0598: JavaSource source = JavaSource.forFileObject(tph
0599: .getFileObject());
0600: source.runUserActionTask(ff = new CompilerTask(tph), true);
0601: return ff.getSimpleName();
0602: } catch (IOException ex) {
0603: throw (RuntimeException) new RuntimeException()
0604: .initCause(ex);
0605: }
0606: }
0607:
0608: public static FileObject getFileObject(final TreePathHandle handle) {
0609: try {
0610: CompilerTask ff;
0611: JavaSource source = JavaSource.forFileObject(handle
0612: .getFileObject());
0613: source.runUserActionTask(ff = new CompilerTask(handle),
0614: true);
0615: return ff.getFileObject();
0616: } catch (IOException ex) {
0617: throw (RuntimeException) new RuntimeException()
0618: .initCause(ex);
0619: }
0620: }
0621:
0622: public static String getQualifiedName(TreePathHandle tph) {
0623: try {
0624: CompilerTask ff;
0625: JavaSource source = JavaSource.forFileObject(tph
0626: .getFileObject());
0627: source.runUserActionTask(ff = new CompilerTask(tph), true);
0628: return ff.getQualifiedName();
0629: } catch (IOException ex) {
0630: throw (RuntimeException) new RuntimeException()
0631: .initCause(ex);
0632: }
0633: }
0634:
0635: public static boolean typeExist(TreePathHandle tph, String fqn) {
0636: try {
0637: CompilerTask ff;
0638: JavaSource source = JavaSource.forFileObject(tph
0639: .getFileObject());
0640: source.runUserActionTask(ff = new CompilerTask(tph, fqn),
0641: true);
0642: return ff.typeExist();
0643: } catch (IOException ex) {
0644: throw (RuntimeException) new RuntimeException()
0645: .initCause(ex);
0646: }
0647: }
0648:
0649: public static ClasspathInfo getClasspathInfoFor(FileObject... files) {
0650: return getClasspathInfoFor(true, files);
0651: }
0652:
0653: public static ClasspathInfo getClasspathInfoFor(
0654: boolean dependencies, FileObject... files) {
0655: return getClasspathInfoFor(dependencies, false, files);
0656: }
0657:
0658: public static ClasspathInfo getClasspathInfoFor(
0659: boolean dependencies, boolean backSource,
0660: FileObject... files) {
0661: assert files.length > 0;
0662: Set<URL> dependentRoots = new HashSet();
0663: for (FileObject fo : files) {
0664: Project p = null;
0665: if (fo != null)
0666: p = FileOwnerQuery.getOwner(fo);
0667: if (p != null) {
0668: URL sourceRoot = URLMapper.findURL(ClassPath
0669: .getClassPath(fo, ClassPath.SOURCE)
0670: .findOwnerRoot(fo), URLMapper.INTERNAL);
0671: if (dependencies) {
0672: dependentRoots.addAll(SourceUtils
0673: .getDependentRoots(sourceRoot));
0674: } else {
0675: dependentRoots.add(sourceRoot);
0676: }
0677: for (SourceGroup root : ProjectUtils.getSources(p)
0678: .getSourceGroups(
0679: JavaProjectConstants.SOURCES_TYPE_JAVA)) {
0680: dependentRoots.add(URLMapper.findURL(root
0681: .getRootFolder(), URLMapper.INTERNAL));
0682: }
0683: } else {
0684: for (ClassPath cp : GlobalPathRegistry.getDefault()
0685: .getPaths(ClassPath.SOURCE)) {
0686: for (FileObject root : cp.getRoots()) {
0687: dependentRoots.add(URLMapper.findURL(root,
0688: URLMapper.INTERNAL));
0689: }
0690: }
0691: }
0692: }
0693:
0694: if (backSource) {
0695: for (FileObject file : files) {
0696: if (file != null) {
0697: ClassPath source = ClassPath.getClassPath(file,
0698: ClassPath.COMPILE);
0699: for (Entry root : source.entries()) {
0700: Result r = SourceForBinaryQuery
0701: .findSourceRoots(root.getURL());
0702: for (FileObject root2 : r.getRoots()) {
0703: dependentRoots.add(URLMapper.findURL(root2,
0704: URLMapper.INTERNAL));
0705: }
0706: }
0707: }
0708: }
0709: }
0710:
0711: ClassPath rcp = ClassPathSupport.createClassPath(dependentRoots
0712: .toArray(new URL[dependentRoots.size()]));
0713: ClassPath nullPath = ClassPathSupport
0714: .createClassPath(new FileObject[0]);
0715: ClassPath boot = files[0] != null ? ClassPath.getClassPath(
0716: files[0], ClassPath.BOOT) : nullPath;
0717: ClassPath compile = files[0] != null ? ClassPath.getClassPath(
0718: files[0], ClassPath.COMPILE) : nullPath;
0719: ClasspathInfo cpInfo = ClasspathInfo.create(boot, compile, rcp);
0720: return cpInfo;
0721: }
0722:
0723: public static ClasspathInfo getClasspathInfoFor(
0724: TreePathHandle... handles) {
0725: FileObject[] result = new FileObject[handles.length];
0726: int i = 0;
0727: for (TreePathHandle handle : handles) {
0728: FileObject fo = getFileObject(handle);
0729: if (i == 0 && fo == null) {
0730: result = new FileObject[handles.length + 1];
0731: result[i++] = handle.getFileObject();
0732: }
0733: result[i++] = fo;
0734: }
0735: return getClasspathInfoFor(result);
0736: }
0737:
0738: /**
0739: * Finds type parameters from <code>typeArgs</code> list that are referenced
0740: * by <code>tm</code> type.
0741: * @param utils compilation type utils
0742: * @param typeArgs modifiable list of type parameters to search; found types will be removed (performance reasons).
0743: * @param result modifiable list that will contain referenced type parameters
0744: * @param tm parametrized type to analyze
0745: */
0746: public static void findUsedGenericTypes(Types utils,
0747: List<TypeMirror> typeArgs, List<TypeMirror> result,
0748: TypeMirror tm) {
0749: if (typeArgs.isEmpty()) {
0750: return;
0751: } else if (tm.getKind() == TypeKind.TYPEVAR) {
0752: TypeVariable type = (TypeVariable) tm;
0753: TypeMirror low = type.getLowerBound();
0754: if (low != null && low.getKind() != TypeKind.NULL) {
0755: findUsedGenericTypes(utils, typeArgs, result, low);
0756: }
0757: TypeMirror up = type.getUpperBound();
0758: if (up != null) {
0759: findUsedGenericTypes(utils, typeArgs, result, up);
0760: }
0761: int index = findTypeIndex(utils, typeArgs, type);
0762: if (index >= 0) {
0763: result.add(typeArgs.remove(index));
0764: }
0765: } else if (tm.getKind() == TypeKind.DECLARED) {
0766: DeclaredType type = (DeclaredType) tm;
0767: for (TypeMirror tp : type.getTypeArguments()) {
0768: findUsedGenericTypes(utils, typeArgs, result, tp);
0769: }
0770: } else if (tm.getKind() == TypeKind.WILDCARD) {
0771: WildcardType type = (WildcardType) tm;
0772: TypeMirror ex = type.getExtendsBound();
0773: if (ex != null) {
0774: findUsedGenericTypes(utils, typeArgs, result, ex);
0775: }
0776: TypeMirror su = type.getSuperBound();
0777: if (su != null) {
0778: findUsedGenericTypes(utils, typeArgs, result, su);
0779: }
0780: }
0781: }
0782:
0783: private static int findTypeIndex(Types utils,
0784: List<TypeMirror> typeArgs, TypeMirror type) {
0785: int i = -1;
0786: for (TypeMirror typeArg : typeArgs) {
0787: i++;
0788: if (utils.isSameType(type, typeArg)) {
0789: return i;
0790: }
0791: }
0792: return -1;
0793: }
0794:
0795: /**
0796: * translates list of elements to list of types
0797: * @param typeParams elements
0798: * @return types
0799: */
0800: public static List<TypeMirror> resolveTypeParamsAsTypes(
0801: List<? extends Element> typeParams) {
0802: if (typeParams.isEmpty()) {
0803: return Collections.<TypeMirror> emptyList();
0804: }
0805: List<TypeMirror> typeArgs = new ArrayList<TypeMirror>(
0806: typeParams.size());
0807: for (Element elm : typeParams) {
0808: typeArgs.add(elm.asType());
0809: }
0810: return typeArgs;
0811: }
0812:
0813: /**
0814: * finds the nearest enclosing ClassTree on <code>path</code> that
0815: * is class or interface or enum or annotation type and is or is not annonymous.
0816: * In case no ClassTree is found the first top level ClassTree is returned.
0817: *
0818: * Especially useful for selecting proper tree to refactor.
0819: *
0820: * @param javac javac
0821: * @param path path to search
0822: * @param isClass stop on class
0823: * @param isInterface stop on interface
0824: * @param isEnum stop on enum
0825: * @param isAnnotation stop on annotation type
0826: * @param isAnonymous check if class or interface is annonymous
0827: * @return path to the enclosing ClassTree
0828: */
0829: public static TreePath findEnclosingClass(CompilationInfo javac,
0830: TreePath path, boolean isClass, boolean isInterface,
0831: boolean isEnum, boolean isAnnotation, boolean isAnonymous) {
0832: Tree selectedTree = path.getLeaf();
0833: TreeUtilities utils = javac.getTreeUtilities();
0834: while (true) {
0835: if (Tree.Kind.CLASS == selectedTree.getKind()) {
0836: ClassTree classTree = (ClassTree) selectedTree;
0837: if (isEnum
0838: && utils.isEnum(classTree)
0839: || isInterface
0840: && utils.isInterface(classTree)
0841: || isAnnotation
0842: && utils.isAnnotation(classTree)
0843: || isClass
0844: && !(utils.isInterface(classTree)
0845: || utils.isEnum(classTree) || utils
0846: .isAnnotation(classTree))) {
0847:
0848: Tree.Kind parentKind = path.getParentPath()
0849: .getLeaf().getKind();
0850: if (isAnonymous
0851: || Tree.Kind.NEW_CLASS != parentKind) {
0852: break;
0853: }
0854: }
0855: }
0856:
0857: path = path.getParentPath();
0858: if (path == null) {
0859: selectedTree = javac.getCompilationUnit()
0860: .getTypeDecls().get(0);
0861: path = javac.getTrees().getPath(
0862: javac.getCompilationUnit(), selectedTree);
0863: break;
0864: }
0865: selectedTree = path.getLeaf();
0866: }
0867: return path;
0868: }
0869:
0870: /**
0871: * Copies javadoc from <code>elm</code> to newly created <code>tree</code>.
0872: * @param elm element containing some javadoc
0873: * @param tree newly created tree where the javadoc should be copied to
0874: * @param wc working copy where the tree belongs to
0875: */
0876: public static void copyJavadoc(Element elm, Tree tree,
0877: WorkingCopy wc) {
0878: TreeMaker make = wc.getTreeMaker();
0879: String jdtxt = wc.getElements().getDocComment(elm);
0880: if (jdtxt != null) {
0881: make.addComment(tree, Comment.create(Comment.Style.JAVADOC,
0882: -1, -1, -1, jdtxt), true);
0883: }
0884: }
0885:
0886: private static class CompilerTask implements
0887: CancellableTask<CompilationController> {
0888:
0889: private FileObject f;
0890: private ElementHandle eh;
0891: private String name;
0892: private String fqn;
0893: private String typeToCheck;
0894: private boolean typeExist;
0895: private ElementHandle<TypeElement> enclosingTypeHandle;
0896: private ElementKind kind;
0897: private IllegalArgumentException iae;
0898:
0899: TreePathHandle handle;
0900:
0901: CompilerTask(TreePathHandle handle) {
0902: this .handle = handle;
0903: }
0904:
0905: CompilerTask(TreePathHandle handle, String fqn) {
0906: this (handle);
0907: typeToCheck = fqn;
0908: }
0909:
0910: public void cancel() {
0911:
0912: }
0913:
0914: public void run(CompilationController cc) {
0915: try {
0916: cc.toPhase(JavaSource.Phase.RESOLVED);
0917: } catch (IOException ex) {
0918: throw (RuntimeException) new RuntimeException()
0919: .initCause(ex);
0920: }
0921: Element el = handle.resolveElement(cc);
0922: f = SourceUtils.getFile(el, cc.getClasspathInfo());
0923: try {
0924: eh = ElementHandle.create(el);
0925: } catch (IllegalArgumentException iae) {
0926: this .iae = iae;
0927: }
0928: name = el.getSimpleName().toString();
0929: if (el instanceof TypeElement)
0930: fqn = ((TypeElement) el).getQualifiedName().toString();
0931: if (typeToCheck != null) {
0932: typeExist = cc.getElements()
0933: .getTypeElement(typeToCheck) != null;
0934: }
0935: if (el instanceof TypeElement) {
0936: enclosingTypeHandle = ElementHandle
0937: .create((TypeElement) el);
0938: } else {
0939: enclosingTypeHandle = ElementHandle.create(SourceUtils
0940: .getEnclosingTypeElement(el));
0941: }
0942:
0943: kind = el.getKind();
0944: }
0945:
0946: public FileObject getFileObject() {
0947: return f;
0948: }
0949:
0950: public ElementHandle getElementHandle() {
0951: return eh;
0952: }
0953:
0954: public String getSimpleName() {
0955: return name;
0956: }
0957:
0958: public String getQualifiedName() {
0959: return fqn;
0960: }
0961:
0962: public boolean typeExist() {
0963: return typeExist;
0964: }
0965:
0966: public ElementHandle<TypeElement> getEnclosingTypeHandle() {
0967: if (iae != null)
0968: throw iae;
0969: return enclosingTypeHandle;
0970: }
0971:
0972: public ElementKind getElementKind() {
0973: return kind;
0974: }
0975: }
0976:
0977: private static class SuperTypesTask implements
0978: CancellableTask<CompilationController> {
0979:
0980: private Collection<FileObject> files;
0981:
0982: TreePathHandle handle;
0983:
0984: SuperTypesTask(TreePathHandle handle) {
0985: this .handle = handle;
0986: }
0987:
0988: public void cancel() {
0989:
0990: }
0991:
0992: public void run(CompilationController cc) {
0993: try {
0994: cc.toPhase(JavaSource.Phase.RESOLVED);
0995: } catch (IOException ex) {
0996: throw (RuntimeException) new RuntimeException()
0997: .initCause(ex);
0998: }
0999: Element el = handle.resolveElement(cc);
1000: files = elementsToFile(getSuperTypes((TypeElement) el, cc,
1001: true), cc.getClasspathInfo());
1002: }
1003:
1004: public Collection<FileObject> getFileObjects() {
1005: return files;
1006: }
1007: }
1008:
1009: /**
1010: * This is a helper method to provide support for delaying invocations of actions
1011: * depending on java model. See <a href="http://java.netbeans.org/ui/waitscanfinished.html">UI Specification</a>.
1012: * <br>Behavior of this method is following:<br>
1013: * If classpath scanning is not in progress, runnable's run() is called. <br>
1014: * If classpath scanning is in progress, modal cancellable notification dialog with specified
1015: * tile is opened.
1016: * </ul>
1017: * As soon as classpath scanning finishes, this dialog is closed and runnable's run() is called.
1018: * This method must be called in AWT EventQueue. Runnable is performed in AWT thread.
1019: *
1020: * @param runnable Runnable instance which will be called.
1021: * @param actionName Title of wait dialog.
1022: * @return true action was cancelled <br>
1023: * false action was performed
1024: */
1025: public static boolean invokeAfterScanFinished(
1026: final Runnable runnable, final String actionName) {
1027: assert SwingUtilities.isEventDispatchThread();
1028: if (SourceUtils.isScanInProgress()) {
1029: final ActionPerformer ap = new ActionPerformer(runnable);
1030: ActionListener listener = new ActionListener() {
1031: public void actionPerformed(ActionEvent e) {
1032: ap.cancel();
1033: waitTask.cancel();
1034: }
1035: };
1036: JLabel label = new JLabel(getString("MSG_WaitScan"),
1037: javax.swing.UIManager
1038: .getIcon("OptionPane.informationIcon"),
1039: SwingConstants.LEFT);
1040: label.setBorder(new EmptyBorder(12, 12, 11, 11));
1041: DialogDescriptor dd = new DialogDescriptor(label,
1042: actionName, true, new Object[] { getString(
1043: "LBL_CancelAction",
1044: new Object[] { actionName }) }, null, 0,
1045: null, listener);
1046: waitDialog = DialogDisplayer.getDefault().createDialog(dd);
1047: waitDialog.pack();
1048: //100ms is workaround for 127536
1049: waitTask = RequestProcessor.getDefault().post(ap, 100);
1050: waitDialog.setVisible(true);
1051: waitTask = null;
1052: waitDialog = null;
1053: return ap.hasBeenCancelled();
1054: } else {
1055: runnable.run();
1056: return false;
1057: }
1058: }
1059:
1060: private static Dialog waitDialog = null;
1061: private static RequestProcessor.Task waitTask = null;
1062:
1063: private static String getString(String key) {
1064: return NbBundle.getMessage(RetoucheUtils.class, key);
1065: }
1066:
1067: private static String getString(String key, Object values) {
1068: return new MessageFormat(getString(key)).format(values);
1069: }
1070:
1071: private static class ActionPerformer implements Runnable {
1072: private Runnable action;
1073: private boolean cancel = false;
1074:
1075: ActionPerformer(Runnable a) {
1076: this .action = a;
1077: }
1078:
1079: public boolean hasBeenCancelled() {
1080: return cancel;
1081: }
1082:
1083: public void run() {
1084: try {
1085: SourceUtils.waitScanFinished();
1086: } catch (InterruptedException ie) {
1087: Exceptions.printStackTrace(ie);
1088: }
1089: SwingUtilities.invokeLater(new Runnable() {
1090: public void run() {
1091: if (!cancel) {
1092: if (waitDialog != null)
1093: waitDialog.setVisible(false);
1094: action.run();
1095: }
1096: }
1097: });
1098: }
1099:
1100: public void cancel() {
1101: assert SwingUtilities.isEventDispatchThread();
1102: // check if the scanning did not finish during cancel
1103: // invocation - in such case do not set cancel to true
1104: // and do not try to hide waitDialog window
1105: if (waitDialog != null) {
1106: cancel = true;
1107: waitDialog.setVisible(false);
1108: }
1109: }
1110: }
1111:
1112: }
|