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-2006 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:
042: package org.netbeans.modules.java.source.ui;
043:
044: import java.io.IOException;
045: import java.util.ArrayList;
046: import java.util.EnumSet;
047: import java.util.HashSet;
048: import java.util.Set;
049: import java.util.concurrent.ExecutionException;
050: import java.util.concurrent.Future;
051: import java.util.logging.Logger;
052: import javax.lang.model.element.TypeElement;
053: import javax.swing.Icon;
054: import org.netbeans.api.java.classpath.ClassPath;
055: import org.netbeans.api.java.classpath.GlobalPathRegistry;
056: import org.netbeans.api.java.classpath.GlobalPathRegistryListener;
057: import org.netbeans.api.java.queries.SourceForBinaryQuery;
058: import org.netbeans.api.java.source.ClassIndex;
059: import org.netbeans.api.java.source.ClasspathInfo;
060: import org.netbeans.api.java.source.CompilationController;
061: import org.netbeans.api.java.source.ElementHandle;
062: import org.netbeans.api.java.source.JavaSource;
063: import org.netbeans.api.java.source.Task;
064: import org.netbeans.api.java.source.ui.TypeElementFinder;
065: import org.netbeans.api.project.FileOwnerQuery;
066: import org.netbeans.api.project.Project;
067: import org.netbeans.api.project.ProjectInformation;
068: import org.netbeans.api.project.ProjectUtils;
069: import org.netbeans.api.project.ui.OpenProjects;
070: import org.netbeans.modules.java.BinaryElementOpen;
071: import org.netbeans.modules.java.source.usages.RepositoryUpdater;
072: import org.netbeans.spi.java.classpath.support.ClassPathSupport;
073: import org.netbeans.spi.jumpto.type.SearchType;
074: import org.netbeans.spi.jumpto.type.TypeProvider;
075: import org.openide.filesystems.FileObject;
076: import org.openide.filesystems.FileStateInvalidException;
077: import org.openide.util.Lookup;
078: import org.openide.util.NbBundle;
079:
080: /**
081: * @author Petr Hrebejk
082: */
083: public class JavaTypeProvider implements TypeProvider {
084: private static final Logger LOGGER = Logger
085: .getLogger(JavaTypeProvider.class.getName());
086: private static final ClassPath EMPTY_CLASSPATH = ClassPathSupport
087: .createClassPath(new FileObject[0]);
088: private Set<CacheItem> cache;
089: private volatile boolean isCanceled = false;
090: private final TypeElementFinder.Customizer customizer;
091: private ClasspathInfo cpInfo;
092: private GlobalPathRegistryListener pathListener;
093:
094: public String name() {
095: return "java"; // NOI18N
096: }
097:
098: public String getDisplayName() {
099: // TODO - i18n
100: return "Java Classes";
101: }
102:
103: public void cleanup() {
104: cache = null;
105: if (pathListener != null)
106: GlobalPathRegistry.getDefault()
107: .removeGlobalPathRegistryListener(pathListener);
108: }
109:
110: public void cancel() {
111: isCanceled = true;
112: }
113:
114: public JavaTypeProvider() {
115: this (null, null);
116: }
117:
118: public JavaTypeProvider(ClasspathInfo cpInfo,
119: TypeElementFinder.Customizer customizer) {
120: this .cpInfo = cpInfo;
121: this .customizer = customizer;
122: }
123:
124: // // This is essentially the code from OpenDeclAction
125: // // TODO: Was OpenDeclAction used for anything else?
126: // public void gotoType(TypeDescriptor type) {
127: // //public void actionPerformed(ActionEvent e) {
128: // Lookup lkp = WindowManager.getDefault().getRegistry().getActivated().getLookup();
129: // DataObject activeFile = (DataObject) lkp.lookup(DataObject.class);
130: // Element value = (Element) lkp.lookup(Element.class);
131: // if (activeFile != null && value != null) {
132: // JavaSource js = JavaSource.forFileObject(activeFile.getPrimaryFile());
133: // if (js != null) {
134: // ClasspathInfo cpInfo = js.getClasspathInfo();
135: // assert cpInfo != null;
136: // UiUtils.open(cpInfo,value);
137: // }
138: // }
139: // }
140:
141: public void computeTypeNames(Context context, Result res) {
142: String text = context.getText();
143: SearchType searchType = context.getSearchType();
144:
145: boolean hasBinaryOpen = Lookup.getDefault().lookup(
146: BinaryElementOpen.class) != null;
147: final ClassIndex.NameKind nameKind;
148: switch (searchType) {
149: case EXACT_NAME:
150: nameKind = ClassIndex.NameKind.SIMPLE_NAME;
151: break;
152: case CASE_INSENSITIVE_EXACT_NAME:
153: nameKind = ClassIndex.NameKind.CASE_INSENSITIVE_REGEXP;
154: break;
155: case PREFIX:
156: nameKind = ClassIndex.NameKind.PREFIX;
157: break;
158: case CASE_INSENSITIVE_PREFIX:
159: nameKind = ClassIndex.NameKind.CASE_INSENSITIVE_PREFIX;
160: break;
161: case REGEXP:
162: nameKind = ClassIndex.NameKind.REGEXP;
163: break;
164: case CASE_INSENSITIVE_REGEXP:
165: nameKind = ClassIndex.NameKind.CASE_INSENSITIVE_REGEXP;
166: break;
167: case CAMEL_CASE:
168: nameKind = ClassIndex.NameKind.CAMEL_CASE;
169: break;
170: default:
171: throw new RuntimeException("Unexpected search type: "
172: + searchType);
173: }
174:
175: long time;
176:
177: long cp, gss, gsb, sfb, gtn, add, sort;
178: cp = gss = gsb = sfb = gtn = add = sort = 0;
179:
180: Future<Project[]> openProjectsTask = OpenProjects.getDefault()
181: .openProjects();
182: try {
183: openProjectsTask.get();
184: } catch (InterruptedException ex) {
185: LOGGER.fine(ex.getMessage());
186: } catch (ExecutionException ex) {
187: LOGGER.fine(ex.getMessage());
188: }
189:
190: if (cache == null) {
191: Set<CacheItem> sources = null;
192:
193: if (cpInfo == null) {
194: // Sources
195: time = System.currentTimeMillis();
196: ClassPath scp = RepositoryUpdater.getDefault()
197: .getScannedSources();
198: FileObject roots[] = scp.getRoots();
199: gss += System.currentTimeMillis() - time;
200: FileObject root[] = new FileObject[1];
201: sources = new HashSet<CacheItem>(roots.length);
202: for (int i = 0; i < roots.length; i++) {
203: root[0] = roots[i];
204: time = System.currentTimeMillis();
205: ClasspathInfo ci = ClasspathInfo.create(
206: EMPTY_CLASSPATH, EMPTY_CLASSPATH,
207: ClassPathSupport.createClassPath(root)); //create(roots[i]);
208: if (isCanceled) {
209: return;
210: } else {
211: sources.add(new CacheItem(roots[i], ci, false));
212: }
213: cp += System.currentTimeMillis() - time;
214: }
215:
216: // Binaries
217: time = System.currentTimeMillis();
218: scp = RepositoryUpdater.getDefault()
219: .getScannedBinaries();
220: roots = scp.getRoots();
221: gsb += System.currentTimeMillis() - time;
222: root = new FileObject[1];
223: for (int i = 0; i < roots.length; i++) {
224: try {
225: if (isCanceled) {
226: return;
227: }
228: time = System.currentTimeMillis();
229: if (!hasBinaryOpen) {
230: SourceForBinaryQuery.Result result = SourceForBinaryQuery
231: .findSourceRoots(roots[i].getURL());
232: if (result.getRoots().length == 0) {
233: continue;
234: }
235: }
236: sfb += System.currentTimeMillis() - time;
237: time = System.currentTimeMillis();
238: root[0] = roots[i];
239: ClasspathInfo ci = ClasspathInfo.create(
240: ClassPathSupport.createClassPath(root),
241: EMPTY_CLASSPATH, EMPTY_CLASSPATH);//create(roots[i]);
242: sources.add(new CacheItem(roots[i], ci, true));
243: cp += System.currentTimeMillis() - time;
244: } catch (FileStateInvalidException e) {
245: continue;
246: } finally {
247: if (isCanceled) {
248: return;
249: }
250: }
251: }
252: } else { // user provided classpath
253:
254: FileObject[] bootRoots = cpInfo.getClassPath(
255: ClasspathInfo.PathKind.BOOT).getRoots();
256: FileObject[] compileRoots = cpInfo.getClassPath(
257: ClasspathInfo.PathKind.COMPILE).getRoots();
258: FileObject[] sourceRoots = cpInfo.getClassPath(
259: ClasspathInfo.PathKind.SOURCE).getRoots();
260: sources = new HashSet<CacheItem>(bootRoots.length
261: + compileRoots.length + sourceRoots.length);
262:
263: // bootPath
264: for (int i = 0; i < bootRoots.length; i++) {
265: time = System.currentTimeMillis();
266: ClasspathInfo ci = ClasspathInfo.create(
267: ClassPathSupport
268: .createClassPath(bootRoots[i]),
269: EMPTY_CLASSPATH, EMPTY_CLASSPATH);
270: if (isCanceled) {
271: return;
272: } else {
273: sources.add(new CacheItem(bootRoots[i], ci,
274: true));
275: }
276: cp += System.currentTimeMillis() - time;
277: }
278:
279: // classPath
280: for (int i = 0; i < compileRoots.length; i++) {
281: time = System.currentTimeMillis();
282: ClasspathInfo ci = ClasspathInfo.create(
283: EMPTY_CLASSPATH, ClassPathSupport
284: .createClassPath(compileRoots[i]),
285: EMPTY_CLASSPATH);
286: if (isCanceled) {
287: return;
288: } else {
289: sources.add(new CacheItem(compileRoots[i], ci,
290: true));
291: }
292: cp += System.currentTimeMillis() - time;
293: }
294:
295: // sourcePath
296: for (int i = 0; i < sourceRoots.length; i++) {
297: time = System.currentTimeMillis();
298: ClasspathInfo ci = ClasspathInfo.create(
299: EMPTY_CLASSPATH, EMPTY_CLASSPATH,
300: ClassPathSupport
301: .createClassPath(sourceRoots[i]));
302: if (isCanceled) {
303: return;
304: } else {
305: sources.add(new CacheItem(sourceRoots[i], ci,
306: false));
307: }
308: cp += System.currentTimeMillis() - time;
309: }
310:
311: }
312:
313: if (!isCanceled) {
314: cache = sources;
315: } else {
316: return;
317: }
318:
319: }
320:
321: ArrayList<JavaTypeDescription> types = new ArrayList<JavaTypeDescription>(
322: cache.size() * 20);
323: Set<ElementHandle<TypeElement>> names = null;
324:
325: boolean scanInProgress;
326: do {
327: // is scan in progress? If so, provide a message to user.
328: scanInProgress = RepositoryUpdater.getDefault()
329: .isScanInProgress();
330: if (scanInProgress) {
331: // ui message
332: String message = NbBundle.getMessage(
333: JavaTypeProvider.class,
334: "LBL_ScanInProgress_warning");
335: res.setMessage(message);
336: } else {
337: res.setMessage(null);
338: }
339:
340: for (final CacheItem ci : cache) {
341: time = System.currentTimeMillis();
342:
343: final String textForQuery;
344: switch (nameKind) {
345: case REGEXP:
346: case CASE_INSENSITIVE_REGEXP:
347: text = removeNonJavaChars(text);
348: String pattern = searchType == SearchType.CASE_INSENSITIVE_EXACT_NAME ? text
349: : text + "*"; // NOI18N
350: pattern = pattern.replace("*", ".*").replace('?',
351: '.');
352: textForQuery = pattern;
353: break;
354: default:
355: textForQuery = text;
356: }
357:
358: if (customizer != null) {
359: names = customizer
360: .query(
361: ci.classpathInfo,
362: textForQuery,
363: nameKind,
364: EnumSet
365: .of(ci.isBinary ? ClassIndex.SearchScope.DEPENDENCIES
366: : ClassIndex.SearchScope.SOURCE));
367: } else {
368: @SuppressWarnings("unchecked")
369: final Set<ElementHandle<TypeElement>>[] n = new Set[1];
370: JavaSource source = JavaSource.create(
371: ci.classpathInfo, new FileObject[0]);
372: try {
373: source.runUserActionTask(
374: new Task<CompilationController>() {
375:
376: public void run(
377: CompilationController parameter)
378: throws Exception {
379: n[0] = ci.classpathInfo
380: .getClassIndex()
381: .getDeclaredTypes(
382: textForQuery,
383: nameKind,
384: EnumSet
385: .of(ci.isBinary ? ClassIndex.SearchScope.DEPENDENCIES
386: : ClassIndex.SearchScope.SOURCE));
387: }
388: }, true);
389: names = n[0];
390: } catch (IOException ex) {
391: LOGGER.fine(ex.getMessage());
392: }
393: }
394:
395: if (isCanceled) {
396: return;
397: }
398:
399: gtn += System.currentTimeMillis() - time;
400: time = System.currentTimeMillis();
401:
402: // Removed because of bad performance To reenable see diff between 1.15 and 1.16
403: // ClassPath.Entry defEntry = ci.getDefiningEntry();
404: for (ElementHandle<TypeElement> name : names) {
405: // Removed because of bad performance To reenable see diff between 1.15 and 1.16
406: // if (defEntry.includes(convertToSourceName(name.getBinaryName()))) {
407: JavaTypeDescription td = new JavaTypeDescription(
408: ci, name);
409: types.add(td);
410: // }
411: if (isCanceled) {
412: return;
413: }
414: }
415: add += System.currentTimeMillis() - time;
416: }
417: // nothing found, wait a while and restart the task
418: // again.
419: if (scanInProgress && types.isEmpty()) {
420: if (RepositoryUpdater.getDefault().isScanInProgress()) {
421: try {
422: Thread.sleep(1000);
423: } catch (InterruptedException ex) {
424: LOGGER.fine(ex.getMessage());
425: }
426: }
427: res.setMessage(null);
428: } else {
429: // finish the loop, results available
430: scanInProgress = false;
431: }
432:
433: } while (scanInProgress);
434:
435: if (!isCanceled) {
436: time = System.currentTimeMillis();
437: // Sorting is now done on the Go To Tpe dialog side
438: // Collections.sort(types);
439: sort += System.currentTimeMillis() - time;
440: LOGGER.fine("PERF - " + " GSS: " + gss + " GSB " + gsb
441: + " CP: " + cp + " SFB: " + sfb + " GTN: " + gtn
442: + " ADD: " + add + " SORT: " + sort);
443: res.addResult(types);
444: }
445:
446: }
447:
448: private static boolean isAllUpper(String text) {
449: for (int i = 0; i < text.length(); i++) {
450: if (!Character.isUpperCase(text.charAt(i))) {
451: return false;
452: }
453: }
454:
455: return true;
456: }
457:
458: private static String removeNonJavaChars(String text) {
459: StringBuilder sb = new StringBuilder();
460:
461: for (int i = 0; i < text.length(); i++) {
462: char c = text.charAt(i);
463: if (Character.isJavaIdentifierPart(c) || c == '*'
464: || c == '?') {
465: sb.append(c);
466: }
467: }
468: return sb.toString();
469: }
470:
471: static class CacheItem {
472:
473: public final boolean isBinary;
474: public final FileObject fileObject;
475: public final ClasspathInfo classpathInfo;
476: public String projectName;
477: public Icon projectIcon;
478: private ClassPath.Entry defEntry;
479:
480: public CacheItem(FileObject fileObject,
481: ClasspathInfo classpathInfo, boolean isBinary) {
482: this .isBinary = isBinary;
483: this .fileObject = fileObject;
484: this .classpathInfo = classpathInfo;
485: }
486:
487: // Removed because of bad performance To reenable see diff between 1.15 and 1.16
488: //
489: // public ClassPath.Entry getDefiningEntry () {
490: // if (defEntry == null) {
491: // ClassPath defCp = ClassPath.getClassPath(fileObject, ClassPath.SOURCE);
492: // if (defCp != null) {
493: // for (ClassPath.Entry e : defCp.entries()) {
494: // if (fileObject.equals(e.getRoot())) {
495: // defEntry = e;
496: // break;
497: // }
498: // }
499: // }
500: // }
501: // return defEntry;
502: // }
503:
504: @Override
505: public int hashCode() {
506: return this .fileObject == null ? 0 : this .fileObject
507: .hashCode();
508: }
509:
510: @Override
511: public boolean equals(Object other) {
512: if (other instanceof CacheItem) {
513: CacheItem otherItem = (CacheItem) other;
514: return this .fileObject == null ? otherItem.fileObject == null
515: : this .fileObject.equals(otherItem.fileObject);
516: }
517: return false;
518: }
519:
520: public FileObject getRoot() {
521: return fileObject;
522: }
523:
524: public boolean isBinary() {
525: return isBinary;
526: }
527:
528: public synchronized String getProjectName() {
529: if (!isBinary && projectName == null) {
530: initProjectInfo();
531: }
532: return projectName;
533: }
534:
535: public synchronized Icon getProjectIcon() {
536: if (!isBinary && projectIcon == null) {
537: initProjectInfo();
538: }
539: return projectIcon;
540: }
541:
542: private void initProjectInfo() {
543: Project p = FileOwnerQuery.getOwner(fileObject);
544: if (p != null) {
545: ProjectInformation pi = ProjectUtils.getInformation(p);
546: projectName = pi.getDisplayName();
547: projectIcon = pi.getIcon();
548: }
549: }
550:
551: }
552: }
|