This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 179508
Collapse All | Expand All

(-)file_not_specified_in_diff (-15 / +1173 lines)
Line  Link Here
0
-- /Users/tomslot/SUN/src/web-main/java.source/src/org/netbeans/api/java/source/ImportsProcesor.java
0
++ /Users/tomslot/SUN/src/web-main/java.source/src/org/netbeans/api/java/source/SourceUtils.java
Lines 1-7 Link Here
1
/*
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
3
 *
4
 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
4
 * Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
5
 *
5
 *
6
 * The contents of this file are subject to the terms of either the GNU
6
 * The contents of this file are subject to the terms of either the GNU
7
 * General Public License Version 2 only ("GPL") or the Common
7
 * General Public License Version 2 only ("GPL") or the Common
Lines 21-26 Link Here
21
 * your own identifying information:
21
 * your own identifying information:
22
 * "Portions Copyrighted [year] [name of copyright owner]"
22
 * "Portions Copyrighted [year] [name of copyright owner]"
23
 *
23
 *
24
 * Contributor(s):
25
 *
26
 * The Original Software is NetBeans. The Initial Developer of the Original
27
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2008 Sun
28
 * Microsystems, Inc. All Rights Reserved.
29
 *
24
 * If you wish your version of this file to be governed by only the CDDL
30
 * If you wish your version of this file to be governed by only the CDDL
25
 * or only the GPL Version 2, indicate your decision by adding
31
 * or only the GPL Version 2, indicate your decision by adding
26
 * "[Contributor] elects to include this software in this distribution
32
 * "[Contributor] elects to include this software in this distribution
Lines 31-59 Link Here
31
 * However, if you add GPL Version 2 code and therefore, elected the GPL
37
 * However, if you add GPL Version 2 code and therefore, elected the GPL
32
 * Version 2 license, then the option applies only if the new code is
38
 * Version 2 license, then the option applies only if the new code is
33
 * made subject to such option by the copyright holder.
39
 * made subject to such option by the copyright holder.
40
 */
41
42
package org.netbeans.api.java.source;
43
44
import java.io.File;
45
import java.io.IOException;
46
import java.net.MalformedURLException;
47
import java.net.URI;
48
import java.net.URL;
49
import java.util.*;
50
51
import javax.lang.model.element.*;
52
import javax.lang.model.element.VariableElement;
53
import javax.lang.model.type.ArrayType;
54
import javax.lang.model.type.DeclaredType;
55
import javax.lang.model.type.TypeKind;
56
import javax.lang.model.type.TypeMirror;
57
import javax.lang.model.type.WildcardType;
58
import javax.lang.model.util.ElementFilter;
59
60
import com.sun.source.tree.*;
61
import com.sun.source.util.TreePath;
62
import com.sun.source.util.TreePathScanner;
63
import com.sun.source.util.Trees;
64
import com.sun.tools.javac.api.JavacTaskImpl;
65
import com.sun.tools.javac.code.Flags;
66
import com.sun.tools.javac.code.Symbol;
67
import com.sun.tools.javac.code.Symbol.*;
68
import com.sun.tools.javac.code.Type;
69
import com.sun.tools.javac.code.Types;
70
import com.sun.tools.javac.comp.Check;
71
import com.sun.tools.javac.model.JavacElements;
72
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
73
import com.sun.tools.javac.util.Context;
74
import java.io.UnsupportedEncodingException;
75
import java.net.URISyntaxException;
76
import java.net.URLEncoder;
77
import java.util.concurrent.Future;
78
import java.util.logging.Level;
79
import java.util.logging.Logger;
80
import javax.swing.SwingUtilities;
81
82
import org.netbeans.api.annotations.common.NonNull;
83
import org.netbeans.api.editor.mimelookup.MimeLookup;
84
import org.netbeans.api.editor.mimelookup.MimePath;
85
import org.netbeans.api.java.classpath.ClassPath;
86
import org.netbeans.api.java.lexer.JavaTokenId;
87
import org.netbeans.api.java.classpath.GlobalPathRegistry;
88
import org.netbeans.api.java.queries.JavadocForBinaryQuery;
89
import org.netbeans.api.java.queries.JavadocForBinaryQuery.Result;
90
import org.netbeans.api.java.queries.SourceForBinaryQuery;
91
import org.netbeans.api.java.source.ClasspathInfo.PathKind;
92
import org.netbeans.api.java.source.JavaSource.Phase;
93
import org.netbeans.api.lexer.TokenHierarchy;
94
import org.netbeans.api.lexer.TokenSequence;
95
import org.netbeans.modules.java.JavaDataLoader;
96
import org.netbeans.modules.java.source.indexing.JavaCustomIndexer;
97
import org.netbeans.modules.java.source.indexing.JavaIndex;
98
import org.netbeans.modules.java.source.parsing.ClasspathInfoProvider;
99
import org.netbeans.modules.java.source.parsing.FileObjects;
100
import org.netbeans.modules.java.source.parsing.JavacParser;
101
import org.netbeans.modules.java.source.usages.ClassIndexImpl;
102
import org.netbeans.modules.java.source.usages.ClassIndexManager;
103
import org.netbeans.modules.java.source.usages.ClasspathInfoAccessor;
104
import org.netbeans.modules.java.source.usages.ExecutableFilesIndex;
105
import org.netbeans.modules.parsing.api.ParserManager;
106
import org.netbeans.modules.parsing.api.ResultIterator;
107
import org.netbeans.modules.parsing.api.UserTask;
108
import org.netbeans.modules.parsing.api.indexing.IndexingManager;
109
import org.netbeans.modules.parsing.impl.indexing.friendapi.IndexingController;
110
import org.netbeans.spi.java.classpath.support.ClassPathSupport;
111
112
import org.openide.filesystems.FileObject;
113
import org.openide.filesystems.FileStateInvalidException;
114
import org.openide.filesystems.FileUtil;
115
import org.openide.filesystems.URLMapper;
116
import org.openide.util.Exceptions;
117
import org.openide.util.Lookup;
118
import org.openide.util.Parameters;
119
import org.openide.util.Utilities;
120
121
/**
34
 *
122
 *
35
 * Contributor(s):
123
 * @author Dusan Balek
124
 */
125
public class SourceUtils {    
126
     
127
    private static final String PACKAGE_SUMMARY = "package-summary";   //NOI18N
128
    
129
    private SourceUtils() {}
130
    
131
    /**
132
     * @since 0.21
133
     */
134
    public static TokenSequence<JavaTokenId> getJavaTokenSequence(final TokenHierarchy hierarchy, final int offset) {
135
        if (hierarchy != null) {
136
            TokenSequence<?> ts = hierarchy.tokenSequence();
137
            while(ts != null && (offset == 0 || ts.moveNext())) {
138
                ts.move(offset);
139
                if (ts.language() == JavaTokenId.language())
140
                    return (TokenSequence<JavaTokenId>)ts;
141
                if (!ts.moveNext() && !ts.movePrevious())
142
                    return null;
143
                ts = ts.embedded();
144
            }
145
        }
146
        return null;
147
    }
148
    
149
    public static boolean checkTypesAssignable(CompilationInfo info, TypeMirror from, TypeMirror to) {
150
        Context c = ((JavacTaskImpl) info.impl.getJavacTask()).getContext();
151
        if (from.getKind() == TypeKind.DECLARED) {
152
            com.sun.tools.javac.util.List<Type> typeVars = com.sun.tools.javac.util.List.nil();
153
            for (TypeMirror tm : ((DeclaredType)from).getTypeArguments()) {
154
                if (tm.getKind() == TypeKind.TYPEVAR)
155
                    typeVars = typeVars.append((Type)tm);
156
            }
157
            if (!typeVars.isEmpty())
158
                from = new Type.ForAll(typeVars, (Type)from);
159
        } else if (from.getKind() == TypeKind.WILDCARD) {
160
            from = Types.instance(c).upperBound((Type)from);
161
        }
162
        return Check.instance(c).checkType(null, (Type)from, (Type)to).getKind() != TypeKind.ERROR;
163
    }
164
    
165
    public static TypeMirror getBound(WildcardType wildcardType) {
166
        Type.TypeVar bound = ((Type.WildcardType)wildcardType).bound;
167
        return bound != null ? bound.bound : null;
168
    }
169
    
170
    /**
171
     * Returns the type element within which this member or constructor
172
     * is declared. Does not accept packages
173
     * If this is the declaration of a top-level type (a non-nested class
174
     * or interface), returns null.
36
 *
175
 *
37
 * Portions Copyrighted 2010 Sun Microsystems, Inc.
176
     * @return the type declaration within which this member or constructor
177
     * is declared, or null if there is none
178
     * @throws IllegalArgumentException if the provided element is a package element
179
     * @deprecated use {@link ElementUtilities#enclosingTypeElement(javax.lang.model.element.Element)}
38
 */
180
 */
181
    public static @Deprecated TypeElement getEnclosingTypeElement( Element element ) throws IllegalArgumentException {
182
        return ElementUtilities.enclosingTypeElementImpl(element);
183
    }
39
184
40
package org.netbeans.api.java.source;
185
    public static TypeElement getOutermostEnclosingTypeElement( Element element ) {
41
186
42
import javax.swing.text.Document;
187
	Element ec =  getEnclosingTypeElement( element );
188
	if (ec == null) {
189
	    ec = element;
190
	}
43
191
192
	while( ec.getEnclosingElement().getKind().isClass() || 
193
	       ec.getEnclosingElement().getKind().isInterface() ) {
194
	
195
	    ec = ec.getEnclosingElement();
196
	}
197
		
198
	return (TypeElement)ec;
199
    }
200
    
201
    /**Resolve full qualified name in the given context. Adds import statement as necessary.
202
     * Returns name that resolved to a given FQN in given context (either simple name
203
     * or full qualified name). Handles import conflicts.
204
     * 
205
     * <br><b>Note:</b> if the <code>info</code> passed to this method is not an instance of {@link WorkingCopy},
206
     * missing import statement is added from a separate modification task executed asynchronously.
207
     * <br><b>Note:</b> after calling this method, it is not permitted to rewrite copy.getCompilationUnit().
208
     * 
209
     * @param info CompilationInfo over which the method should work
210
     * @param context in which the fully qualified should be resolved
211
     * @param fqn the fully qualified name to resolve
212
     * @return either a simple name or a FQN that will resolve to given fqn in given context
213
     */
214
    public static String resolveImport(final CompilationInfo info, final TreePath context, final String fqn) throws NullPointerException, IOException {
215
        if (info == null)
216
            throw new NullPointerException();
217
        if (context == null)
218
            throw new NullPointerException();
219
        if (fqn == null)
220
            throw new NullPointerException();
221
        
222
        CompilationUnitTree cut = info.getCompilationUnit();
223
        final Trees trees = info.getTrees();
224
        final Scope scope = trees.getScope(context);
225
        String qName = fqn;
226
        StringBuilder sqName = new StringBuilder();
227
        String sName = null;
228
        boolean clashing = false;
229
        ElementUtilities eu = info.getElementUtilities();
230
        ElementUtilities.ElementAcceptor acceptor = new ElementUtilities.ElementAcceptor() {
231
            public boolean accept(Element e, TypeMirror type) {
232
                return (e.getKind().isClass() || e.getKind().isInterface()) && trees.isAccessible(scope, (TypeElement)e);
233
            }
234
        };
235
        while(qName != null && qName.length() > 0) {
236
            int lastDot = qName.lastIndexOf('.');
237
            String simple = qName.substring(lastDot < 0 ? 0 : lastDot + 1);
238
            if (sName == null)
239
                sName = simple;
240
            else
241
                sqName.insert(0, '.');
242
            sqName.insert(0, simple);
243
            if (info.getElements().getTypeElement(qName) != null) {
244
                boolean matchFound = false;
245
                for(Element e : eu.getLocalMembersAndVars(scope, acceptor)) {
246
                    if (simple.contentEquals(e.getSimpleName())) {
247
                        //either a clash or already imported:
248
                        if (qName.contentEquals(((TypeElement)e).getQualifiedName())) {
249
                            return sqName.toString();
250
                        } else if (fqn == qName) {
251
                            clashing = true;
252
                        }
253
                        matchFound = true;
254
                        break;
255
                    }
256
                }
257
                if (!matchFound) {
258
                    for(TypeElement e : eu.getGlobalTypes(acceptor)) {
259
                        if (simple.contentEquals(e.getSimpleName())) {
260
                            //either a clash or already imported:
261
                            if (qName.contentEquals(e.getQualifiedName())) {
262
                                return sqName.toString();
263
                            } else if (fqn == qName) {
264
                                clashing = true;
265
                            }
266
                            break;
267
                        }
268
                    }
269
                }
270
            }
271
            qName = lastDot < 0 ? null : qName.substring(0, lastDot);
272
        }
273
        if (clashing)
274
            return fqn;
275
        
276
        //not imported/visible so far by any means:
277
        String topLevelLanguageMIMEType = info.getFileObject().getMIMEType();
278
        if ("text/x-java".equals(topLevelLanguageMIMEType)){
279
            if (info instanceof WorkingCopy) {
280
                CompilationUnitTree nue = (CompilationUnitTree) ((WorkingCopy)info).getChangeSet().get(cut);
281
                cut = nue != null ? nue : cut;
282
                ((WorkingCopy)info).rewrite(info.getCompilationUnit(), addImports(cut, Collections.singletonList(fqn), ((WorkingCopy)info).getTreeMaker()));
283
            } else {
284
                SwingUtilities.invokeLater(new Runnable() {
285
                    public void run() {
286
                        try {
287
                            ModificationResult.runModificationTask(Collections.singletonList(info.getSnapshot().getSource()), new UserTask() {
288
                                @Override
289
                                public void run(ResultIterator resultIterator) throws Exception {
290
                                    WorkingCopy copy = WorkingCopy.get(resultIterator.getParserResult());
291
                                    copy.toPhase(Phase.ELEMENTS_RESOLVED);
292
                                    copy.rewrite(copy.getCompilationUnit(), addImports(copy.getCompilationUnit(), Collections.singletonList(fqn), copy.getTreeMaker()));
293
                                }
294
                            }).commit();
295
                        } catch (Exception e) {
296
                            Exceptions.printStackTrace(e);
297
                        }
298
                    }
299
                });
300
            }
301
        } else { // embedded java, look up the handler for the top level language
302
            Lookup lookup = MimeLookup.getLookup(MimePath.get(topLevelLanguageMIMEType));
303
            Lookup.Result result = lookup.lookup(new Lookup.Template(ImportsProcesor.class));
304
            Collection<ImportsProcesor> instances = result.allInstances();
305
306
            for (ImportsProcesor importsProcesor : instances) {
307
                importsProcesor.addImport(info.getDocument(), fqn);
308
            }
309
            
310
        }
311
        TypeElement te = info.getElements().getTypeElement(fqn);
312
        if (te != null) {
313
            JCCompilationUnit unit = (JCCompilationUnit) info.getCompilationUnit();
314
            unit.namedImportScope = unit.namedImportScope.dupUnshared();
315
            unit.namedImportScope.enterIfAbsent((Symbol) te);
316
        }        
317
        return sName;
318
    }
319
    
44
/**
320
/**
45
 * Interface that allows to hook custom code to the action of inserting import
46
 * when code completion is called within embedded Java code
47
 *
321
 *
48
 * @author Tomasz.Slota@Sun.COM
322
     *
49
 */
323
 */
50
public interface ImportsProcesor {
324
    private static CompilationUnitTree addImports(CompilationUnitTree cut, List<String> toImport, TreeMaker make)
325
        throws IOException {
326
        // do not modify the list given by the caller (may be reused or immutable).
327
        toImport = new ArrayList<String>(toImport); 
328
        Collections.sort(toImport);
329
330
        List<ImportTree> imports = new ArrayList<ImportTree>(cut.getImports());
331
        int currentToImport = toImport.size() - 1;
332
        int currentExisting = imports.size() - 1;
333
        
334
        while (currentToImport >= 0 && currentExisting >= 0) {
335
            String currentToImportText = toImport.get(currentToImport);
336
            
337
            while (currentExisting >= 0 && (imports.get(currentExisting).isStatic() || imports.get(currentExisting).getQualifiedIdentifier().toString().compareTo(currentToImportText) > 0))
338
                currentExisting--;
339
            
340
            if (currentExisting >= 0) {
341
                imports.add(currentExisting+1, make.Import(make.Identifier(currentToImportText), false));
342
                currentToImport--;
343
            }
344
        }
345
        // we are at the head of import section and we still have some imports
346
        // to add, put them to the very beginning
347
        while (currentToImport >= 0) {
348
            String importText = toImport.get(currentToImport);
349
            imports.add(0, make.Import(make.Identifier(importText), false));
350
            currentToImport--;
351
        }
352
        // return a copy of the unit with changed imports section
353
        return make.CompilationUnit(cut.getPackageName(), imports, cut.getTypeDecls(), cut.getSourceFile());
354
    }
355
51
    /**
356
    /**
52
     * Handle request to add unresolved import
357
     * Returns a {@link FileObject} in which the Element is defined.
53
     *  (top-level-language specific way)
358
     * @param element for which the {@link FileObject} should be located
359
     * @param cpInfo the classpaths context
360
     * @return the defining {@link FileObject} or null if it cannot be
361
     * found
54
     *
362
     *
55
     * @param doc
363
     * @deprecated use {@link getFile(ElementHandle, ClasspathInfo)}
56
     * @param fullyQualifiedClassName
57
     */
364
     */
58
    void addImport(Document doc, String fullyQualifiedClassName);
365
    public static FileObject getFile (Element element, final ClasspathInfo cpInfo) {
366
        Parameters.notNull("element", element); //NOI18N
367
        Parameters.notNull("cpInfo", cpInfo);   //NOI18N
368
        
369
        Element prev = element.getKind() == ElementKind.PACKAGE ? element : null;
370
        while (element.getKind() != ElementKind.PACKAGE) {
371
            prev = element;
372
            element = element.getEnclosingElement();
59
}
373
}
374
        final ElementKind kind = prev.getKind();
375
        if (!(kind.isClass() || kind.isInterface() || kind == ElementKind.PACKAGE)) {
376
            return null;
377
        }        
378
        final ElementHandle<? extends Element> handle = ElementHandle.create(prev);
379
        return getFile (handle, cpInfo);
380
    }
381
    
382
    /**
383
     * Returns a {@link FileObject} of the source file in which the handle is declared.
384
     * @param handle to find the {@link FileObject} for
385
     * @param cpInfo classpaths for resolving handle
386
     * @return {@link FileObject} or null when the source file cannot be found
387
     */
388
    public static FileObject getFile (final ElementHandle<? extends Element> handle, final ClasspathInfo cpInfo) {
389
        Parameters.notNull("handle", handle);
390
        Parameters.notNull("cpInfo", cpInfo);        
391
        try {
392
            boolean pkg = handle.getKind() == ElementKind.PACKAGE;
393
            String[] signature = handle.getSignature();
394
            assert signature.length >= 1;
395
            ClassPath cp = ClassPathSupport.createProxyClassPath(
396
                new ClassPath[] {
397
                    cpInfo.getClassPath(ClasspathInfo.PathKind.SOURCE),
398
                    createClassPath(cpInfo,ClasspathInfo.PathKind.OUTPUT),
399
                    createClassPath(cpInfo,ClasspathInfo.PathKind.BOOT),                    
400
                    createClassPath(cpInfo,ClasspathInfo.PathKind.COMPILE),
401
                });
402
           String pkgName, className = null;
403
            if (pkg) {
404
                pkgName = FileObjects.convertPackage2Folder(signature[0]);
405
            }
406
            else {
407
                int index = signature[0].lastIndexOf('.');                          //NOI18N
408
                if (index<0) {
409
                    pkgName = "";                                             //NOI18N
410
                    className = signature[0];
411
                }
412
                else {
413
                    pkgName = FileObjects.convertPackage2Folder(signature[0].substring(0,index));
414
                    className = signature[0].substring(index+1);
415
                }
416
            }
417
            List<FileObject> fos = cp.findAllResources(pkgName);
418
            for (FileObject fo : fos) {
419
                FileObject root = cp.findOwnerRoot(fo);
420
                assert root != null;
421
                FileObject[] sourceRoots = SourceForBinaryQuery.findSourceRoots(root.getURL()).getRoots();                        
422
                ClassPath sourcePath = ClassPathSupport.createClassPath(sourceRoots);
423
                LinkedList<FileObject> folders = new LinkedList<FileObject>(sourcePath.findAllResources(pkgName));
424
                if (pkg) {
425
                    return folders.isEmpty() ? fo : folders.get(0);
426
                }
427
                else {               
428
                    boolean caseSensitive = isCaseSensitive ();
429
                    String sourceFileName = getSourceFileName (className);
430
                    folders.addFirst(fo);
431
                    for (FileObject folder : folders) {
432
                        FileObject[] children = folder.getChildren();
433
                        for (FileObject child : children) {
434
                            if (((caseSensitive && child.getName().equals (sourceFileName)) ||
435
                                (!caseSensitive && child.getName().equalsIgnoreCase (sourceFileName))) &&
436
                                (child.isData() && JavaDataLoader.JAVA_EXTENSION.equalsIgnoreCase(child.getExt()))) {
437
                                return child;
438
                            }
439
                        }
440
                    }
441
                    FileObject foundFo;
442
                    if (sourceRoots.length == 0) {
443
                        foundFo = findSource (signature[0],root);
444
                    }
445
                    else {
446
                        foundFo = findSource (signature[0],sourceRoots);
447
                    }
448
                    if (foundFo != null) {
449
                        return foundFo;
450
                    }
451
                }
452
            }
453
        } catch (IOException e) {
454
            Exceptions.printStackTrace(e);
455
        }
456
        return null;        
457
    }
458
    
459
    private static FileObject findSource (final String binaryName, final FileObject... fos) throws IOException {
460
        final ClassIndexManager cim = ClassIndexManager.getDefault();
461
        try {
462
            return cim.readLock(new ClassIndexManager.ExceptionAction<FileObject>() {
463
464
                public FileObject run() throws IOException, InterruptedException {
465
                    for (FileObject fo : fos) {
466
                        ClassIndexImpl ci = cim.getUsagesQuery(fo.getURL());
467
                        if (ci != null) {
468
                            String sourceName = ci.getSourceName(binaryName);
469
                            if (sourceName != null) {
470
                                FileObject result = fo.getFileObject(sourceName);
471
                                if (result != null) {
472
                                    return result;
473
                                }
474
                            }
475
                        }
476
                    }
477
                    return null;
478
                }
479
            });
480
        } catch (InterruptedException e) {
481
            //Never thrown
482
            Exceptions.printStackTrace(e);
483
            return null;
484
        }
485
    }
486
    
487
    /**
488
     * Finds {@link URL} of a javadoc page for given element when available. This method 
489
     * uses {@link JavadocForBinaryQuery} to find the javadoc page for the give element.
490
     * For {@link PackageElement} it returns the package-summary.html for given package.
491
     * @param element to find the Javadoc for
492
     * @param cpInfo classpaths used to resolve
493
     * @return the URL of the javadoc page or null when the javadoc is not available.
494
     */
495
    @org.netbeans.api.annotations.common.SuppressWarnings(value={"DMI_COLLECTION_OF_URLS"}/*,justification="URLs have never host part"*/)    //NOI18N
496
    public static URL getJavadoc (final Element element, final ClasspathInfo cpInfo) {      
497
        if (element == null || cpInfo == null) {
498
            throw new IllegalArgumentException ("Cannot pass null as an argument of the SourceUtils.getJavadoc");  //NOI18N
499
        }
500
        
501
        ClassSymbol clsSym = null;
502
        String pkgName;
503
        String pageName;
504
        boolean buildFragment = false;
505
        if (element.getKind() == ElementKind.PACKAGE) {
506
            List<? extends Element> els = element.getEnclosedElements();            
507
            for (Element e :els) {
508
                if (e.getKind().isClass() || e.getKind().isInterface()) {
509
                    clsSym = (ClassSymbol) e;
510
                    break;
511
                }
512
            }
513
            if (clsSym == null) {
514
                return null;
515
            }
516
            pkgName = FileObjects.convertPackage2Folder(((PackageElement)element).getQualifiedName().toString());
517
            pageName = PACKAGE_SUMMARY;
518
        }
519
        else {
520
            Element e = element;
521
            StringBuilder sb = new StringBuilder();
522
            while(e.getKind() != ElementKind.PACKAGE) {
523
                if (e.getKind().isClass() || e.getKind().isInterface()) {
524
                    if (sb.length() > 0)
525
                        sb.insert(0, '.');
526
                    sb.insert(0, e.getSimpleName());
527
                    if (clsSym == null)
528
                        clsSym = (ClassSymbol)e;
529
                }
530
                e = e.getEnclosingElement();
531
            }
532
            if (clsSym == null)
533
                return null;
534
            pkgName = FileObjects.convertPackage2Folder(((PackageElement)e).getQualifiedName().toString());
535
            pageName = sb.toString();
536
            buildFragment = element != clsSym;
537
        }
538
        
539
        if (clsSym.completer != null) {
540
            clsSym.complete();
541
        }
542
        
543
        URL sourceRoot = null;
544
        Set<URL> binaries = new HashSet<URL>();        
545
        try {
546
            if (clsSym.classfile != null) {
547
                FileObject  fo = URLMapper.findFileObject(clsSym.classfile.toUri().toURL());
548
                StringTokenizer tk = new StringTokenizer(pkgName,"/");             //NOI18N
549
                for (int i=0 ;fo != null && i<=tk.countTokens(); i++) {
550
                    fo = fo.getParent();
551
                }
552
                if (fo != null) {
553
                    URL url = fo.getURL();
554
                    sourceRoot = JavaIndex.getSourceRootForClassFolder(url);
555
                    if (sourceRoot == null) {
556
                        binaries.add(url);
557
                    } else {
558
                        // sourceRoot may be a class root in reality
559
                        binaries.add(sourceRoot);
560
                    }
561
                }
562
            }
563
            if (sourceRoot != null) {
564
                FileObject sourceFo = URLMapper.findFileObject(sourceRoot);
565
                if (sourceFo != null) {
566
                    ClassPath exec = ClassPath.getClassPath(sourceFo, ClassPath.EXECUTE);
567
                    ClassPath compile = ClassPath.getClassPath(sourceFo, ClassPath.COMPILE);
568
                    ClassPath source = ClassPath.getClassPath(sourceFo, ClassPath.SOURCE);
569
                    if (exec == null) {
570
                        exec = compile;
571
                        compile = null;
572
                    }
573
                    if (exec != null && source != null) {
574
                        Set<URL> roots = new HashSet<URL>();
575
                        for (ClassPath.Entry e : exec.entries()) {
576
                            roots.add(e.getURL());
577
                        }
578
                        if (compile != null) {
579
                            for (ClassPath.Entry e : compile.entries()) {
580
                                roots.remove(e.getURL());
581
                            }
582
                        }
583
                        List<FileObject> sourceRoots = Arrays.asList(source.getRoots());
584
out:                    for (URL e : roots) {
585
                            FileObject[] res = SourceForBinaryQuery.findSourceRoots(e).getRoots();
586
                            for (FileObject fo : res) {
587
                                if (sourceRoots.contains(fo)) {
588
                                    binaries.add(e);
589
                                    continue out;
590
                                }
591
                            }
592
                        }
593
                    }
594
                }
595
            }
596
            for (URL binary : binaries) {
597
                Result javadocResult = JavadocForBinaryQuery.findJavadoc(binary);
598
                URL[] result = javadocResult.getRoots();
599
                for (int cntr = 0; cntr < result.length; cntr++) {
600
                    if (!result[cntr].toExternalForm().endsWith("/")) { // NOI18N
601
                        Logger.getLogger(SourceUtils.class.getName()).log(Level.WARNING, "JavadocForBinaryQuery.Result: {0} returned non-folder URL: {1}, ignoring", new Object[] {javadocResult.getClass(), result[cntr].toExternalForm()});
602
                        result[cntr] = null;
603
                    }
604
                }
605
                ClassPath cp = ClassPathSupport.createClassPath(result);
606
                FileObject fo = cp.findResource(pkgName);
607
                if (fo != null) {
608
                    for (FileObject child : fo.getChildren()) {
609
                        if (pageName.equals(child.getName()) && FileObjects.HTML.equalsIgnoreCase(child.getExt())) {
610
                            URL url = child.getURL();
611
                            CharSequence fragment = null;
612
                            if (url != null && buildFragment) {
613
                                fragment = getFragment(element);
614
                            }
615
                            if (fragment != null && fragment.length() > 0) {
616
                                try {
617
                                    // Javadoc fragments may contain chars that must be escaped to comply with RFC 2396.
618
                                    // Unfortunately URLEncoder escapes almost everything but
619
                                    // spaces replaces with '+' char which is wrong so it is
620
                                    // replaced with "%20"escape sequence here.
621
                                    String encodedfragment = URLEncoder.encode(fragment.toString(), "UTF-8"); // NOI18N
622
                                    encodedfragment = encodedfragment.replace("+", "%20"); // NOI18N
623
                                    return new URI(url.toExternalForm() + '#' + encodedfragment).toURL();
624
                                } catch (URISyntaxException ex) {
625
                                    Exceptions.printStackTrace(ex);
626
                                } catch (UnsupportedEncodingException ex) {
627
                                    Exceptions.printStackTrace(ex);
628
                                } catch (MalformedURLException ex) {
629
                                    Exceptions.printStackTrace(ex);
630
                                }
631
                            }
632
                            return url;
633
                        }
634
                    }
635
                }
636
            }
637
            
638
        } catch (MalformedURLException e) {
639
            Exceptions.printStackTrace(e);
640
        }
641
        catch (FileStateInvalidException e) {
642
            Exceptions.printStackTrace(e);
643
        }
644
        return null;
645
    }
646
    
647
    private static CharSequence getFragment(Element e) {
648
        StringBuilder sb = new StringBuilder();
649
        if (!e.getKind().isClass() && !e.getKind().isInterface()) {
650
            if (e.getKind() == ElementKind.CONSTRUCTOR) {
651
                sb.append(e.getEnclosingElement().getSimpleName());
652
            } else {
653
                sb.append(e.getSimpleName());
654
            }
655
            if (e.getKind() == ElementKind.METHOD || e.getKind() == ElementKind.CONSTRUCTOR) {
656
                ExecutableElement ee = (ExecutableElement)e;
657
                sb.append('('); //NOI18N
658
                for (Iterator<? extends VariableElement> it = ee.getParameters().iterator(); it.hasNext();) {
659
                    VariableElement param = it.next();
660
                    appendType(sb, param.asType(), ee.isVarArgs() && !it.hasNext());
661
                    if (it.hasNext())
662
                        sb.append(", ");
663
                }
664
                sb.append(')'); //NOI18N
665
            }
666
        }
667
        return sb;
668
    }
669
    
670
    private static void appendType(StringBuilder sb, TypeMirror type, boolean varArg) {
671
        switch (type.getKind()) {
672
            case ARRAY:
673
                appendType(sb, ((ArrayType)type).getComponentType(), false);
674
                sb.append(varArg ? "..." : "[]"); //NOI18N
675
                break;
676
            case DECLARED:
677
                sb.append(((TypeElement)((DeclaredType)type).asElement()).getQualifiedName());
678
                break;
679
            default:
680
                sb.append(type);
681
        }
682
    }
683
    
684
    /**
685
     * Tests whether the initial scan is in progress.
686
     */
687
    public static boolean isScanInProgress () {
688
        return IndexingManager.getDefault().isIndexing();
689
    }
690
691
    /**
692
     * Waits for the end of the initial scan, this helper method 
693
     * is designed for tests which require to wait for end of initial scan.
694
     * @throws InterruptedException is thrown when the waiting thread is interrupted.
695
     * @deprecated use {@link JavaSource#runWhenScanFinished}
696
     */
697
    public static void waitScanFinished () throws InterruptedException {
698
        try {
699
            class T extends UserTask implements ClasspathInfoProvider {
700
                private final ClassPath EMPTY_PATH = ClassPathSupport.createClassPath(new URL[0]);
701
                private final ClasspathInfo cpinfo = ClasspathInfo.create(EMPTY_PATH, EMPTY_PATH, EMPTY_PATH);
702
                @Override
703
                public void run(ResultIterator resultIterator) throws Exception {
704
                    // no-op
705
                }
706
707
                public ClasspathInfo getClasspathInfo() {
708
                    return cpinfo;
709
                }
710
            }
711
            Future<Void> f = ParserManager.parseWhenScanFinished(JavacParser.MIME_TYPE, new T());
712
            if (!f.isDone()) {
713
                f.get();
714
            }
715
        } catch (Exception ex) {
716
        }
717
    }
718
    
719
    
720
    /**
721
     * Returns the dependent source path roots for given source root.
722
     * It returns all the open project source roots which have either
723
     * direct or transitive dependency on the given source root.
724
     * @param root to find the dependent roots for
725
     * @return {@link Set} of {@link URL}s containing at least the
726
     * incoming root, never returns null.
727
     * @since 0.10
728
     */
729
    @org.netbeans.api.annotations.common.SuppressWarnings(value={"DMI_COLLECTION_OF_URLS"}/*,justification="URLs have never host part"*/)    //NOI18N
730
    public static Set<URL> getDependentRoots (final URL root) {
731
        final Map<URL, List<URL>> deps = IndexingController.getDefault().getRootDependencies();
732
        return getDependentRootsImpl (root, deps);
733
    }
734
    
735
736
    @org.netbeans.api.annotations.common.SuppressWarnings(value={"DMI_COLLECTION_OF_URLS"}/*,justification="URLs have never host part"*/)    //NOI18N
737
    static Set<URL> getDependentRootsImpl (final URL root, final Map<URL, List<URL>> deps) {
738
        //Create inverse dependencies        
739
        final Map<URL, List<URL>> inverseDeps = new HashMap<URL, List<URL>> ();
740
        for (Map.Entry<URL,List<URL>> entry : deps.entrySet()) {
741
            final URL u1 = entry.getKey();
742
            final List<URL> l1 = entry.getValue();
743
            for (URL u2 : l1) {
744
                List<URL> l2 = inverseDeps.get(u2);
745
                if (l2 == null) {
746
                    l2 = new ArrayList<URL>();
747
                    inverseDeps.put (u2,l2);
748
                }
749
                l2.add (u1);
750
            }
751
        }
752
        //Collect dependencies
753
        final Set<URL> result = new HashSet<URL>();
754
        final LinkedList<URL> todo = new LinkedList<URL> ();
755
        todo.add (root);
756
        while (!todo.isEmpty()) {
757
            final URL u = todo.removeFirst();
758
            if (!result.contains(u)) {
759
                result.add (u);
760
                final List<URL> ideps = inverseDeps.get(u);
761
                if (ideps != null) {
762
                    todo.addAll (ideps);
763
                }
764
            }
765
        }
766
        //Filter non opened projects
767
        Set<ClassPath> cps = GlobalPathRegistry.getDefault().getPaths(ClassPath.SOURCE);
768
        Set<URL> toRetain = new HashSet<URL>();
769
        for (ClassPath cp : cps) {
770
            for (ClassPath.Entry e : cp.entries()) {
771
                toRetain.add(e.getURL());
772
            }
773
        }
774
        result.retainAll(toRetain);
775
        return result;
776
    }    
777
    
778
    //Helper methods    
779
    
780
    /**
781
     * Returns classes declared in the given source file which have the main method.
782
     * @param fo source file
783
     * @return the classes containing main method
784
     * @throws IllegalArgumentException when file does not exist or is not a java source file.
785
     */
786
    public static Collection<ElementHandle<TypeElement>> getMainClasses (final FileObject fo) {
787
        if (fo == null || !fo.isValid() || fo.isVirtual()) {
788
            throw new IllegalArgumentException ();
789
        }
790
        final JavaSource js = JavaSource.forFileObject(fo);        
791
        if (js == null) {
792
            throw new IllegalArgumentException ();
793
        }
794
        try {
795
            final List<ElementHandle<TypeElement>> result = new LinkedList<ElementHandle<TypeElement>>();
796
            js.runUserActionTask(new Task<CompilationController>() {            
797
                public void run(final CompilationController control) throws Exception {
798
                    if (control.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED).compareTo (JavaSource.Phase.ELEMENTS_RESOLVED)>=0) {
799
                        new TreePathScanner<Void,Void> () {
800
                           public @Override Void visitMethod(MethodTree node, Void p) {
801
                               ExecutableElement method = (ExecutableElement) control.getTrees().getElement(getCurrentPath());
802
                               if (method != null && SourceUtils.isMainMethod(method) && isAccessible(method.getEnclosingElement())) {
803
                                   result.add (ElementHandle.create((TypeElement)method.getEnclosingElement()));
804
                               }
805
                               return null;
806
                           }
807
                        }.scan(control.getCompilationUnit(), null);
808
                    }                   
809
                }
810
811
                private boolean isAccessible (Element element) {
812
                    ElementKind kind = element.getKind();
813
                    while (kind != ElementKind.PACKAGE) {
814
                        if (!kind.isClass() && !kind.isInterface()) {
815
                            return false;
816
                        }                    
817
                        Set<Modifier> modifiers = ((TypeElement)element).getModifiers();
818
                        Element parent = element.getEnclosingElement();
819
                        if (parent.getKind() != ElementKind.PACKAGE && !modifiers.contains(Modifier.STATIC)) {
820
                            return false;
821
                        }
822
                        element = parent;
823
                        kind = element.getKind();
824
                    }
825
                    return true;
826
                }
827
828
            }, true);
829
            return result;
830
        } catch (IOException ioe) {
831
            Exceptions.printStackTrace(ioe);
832
            return Collections.<ElementHandle<TypeElement>>emptySet();
833
        }		
834
    }
835
    
836
    /**
837
     * Returns true when the class contains main method.
838
     * @param qualifiedName the fully qualified name of class
839
     * @param cpInfo the classpath used to resolve the class
840
     * @return true when the class contains a main method
841
     */
842
    public static boolean isMainClass (final String qualifiedName, ClasspathInfo cpInfo) {
843
        if (qualifiedName == null || cpInfo == null) {
844
            throw new IllegalArgumentException ();
845
        }
846
        final boolean[] result = new boolean[]{false};
847
        JavaSource js = JavaSource.create(cpInfo);
848
        try {
849
            js.runUserActionTask(new Task<CompilationController>() {
850
851
                public void run(CompilationController control) throws Exception {
852
                    TypeElement type = ((JavacElements)control.getElements()).getTypeElementByBinaryName(qualifiedName);
853
                    if (type == null) {
854
                        return;
855
                    }
856
                    List<? extends ExecutableElement> methods = ElementFilter.methodsIn(type.getEnclosedElements());
857
                    for (ExecutableElement method : methods) {
858
                        if (SourceUtils.isMainMethod(method)) {
859
                            result[0] = true;
860
                            break;
861
                        }
862
                    }
863
                }
864
865
            }, true);
866
        } catch (IOException ioe) {
867
            Exceptions.printStackTrace(ioe);
868
        }
869
        return result[0];
870
    }
871
    
872
    /**
873
     * Returns true if the method is a main method
874
     * @param method to be checked
875
     * @return true when the method is a main method
876
     */
877
    public static boolean isMainMethod (final ExecutableElement method) {
878
        if (!"main".contentEquals(method.getSimpleName())) {                //NOI18N
879
            return false;
880
        }
881
        long flags = ((Symbol.MethodSymbol)method).flags();                 //faster
882
        if (((flags & Flags.PUBLIC) == 0) || ((flags & Flags.STATIC) == 0)) {
883
            return false;
884
        }
885
        if (method.getReturnType().getKind() != TypeKind.VOID) {
886
            return false;
887
        }
888
        List<? extends VariableElement> params = method.getParameters();
889
        if (params.size() != 1) {
890
            return false;
891
        }
892
        TypeMirror param = params.get(0).asType();
893
        if (param.getKind() != TypeKind.ARRAY) {
894
            return false;
895
        }
896
        ArrayType array = (ArrayType) param;
897
        TypeMirror compound = array.getComponentType();
898
        if (compound.getKind() != TypeKind.DECLARED) {
899
            return false;
900
        }
901
        if (!"java.lang.String".contentEquals(((TypeElement)((DeclaredType)compound).asElement()).getQualifiedName())) {   //NOI18N
902
            return false;
903
        }
904
        return true;
905
    }
906
    
907
    /**
908
     * Returns classes declared under the given source roots which have the main method.
909
     * @param sourceRoots the source roots
910
     * @return the classes containing the main methods
911
     * Currently this method is not optimized and may be slow
912
     */
913
    public static Collection<ElementHandle<TypeElement>> getMainClasses (final FileObject[] sourceRoots) {
914
        final List<ElementHandle<TypeElement>> result = new LinkedList<ElementHandle<TypeElement>> ();
915
        for (final FileObject root : sourceRoots) {
916
            try {               
917
                final File rootFile = FileUtil.toFile(root);
918
                ClassPath bootPath = ClassPath.getClassPath(root, ClassPath.BOOT);
919
                ClassPath compilePath = ClassPath.getClassPath(root, ClassPath.COMPILE);
920
                ClassPath srcPath = ClassPathSupport.createClassPath(new FileObject[] {root});
921
                ClasspathInfo cpInfo = ClasspathInfo.create(bootPath, compilePath, srcPath);
922
                JavaSource js = JavaSource.create(cpInfo);
923
                js.runUserActionTask(new Task<CompilationController>() {
924
                    public void run(CompilationController control) throws Exception {
925
                        final URL rootURL = root.getURL();
926
                        Iterable<? extends URL> mainClasses = ExecutableFilesIndex.DEFAULT.getMainClasses(rootURL);                        
927
                        List<ElementHandle<TypeElement>> classes = new LinkedList<ElementHandle<TypeElement>>();
928
                        for (URL mainClass : mainClasses) {
929
                            File mainFo = new File (URI.create(mainClass.toExternalForm()));
930
                            if (mainFo.exists()) {
931
                                classes.addAll(JavaCustomIndexer.getRelatedTypes(mainFo, rootFile));
932
                            }
933
                        }
934
                        for (ElementHandle<TypeElement> cls : classes) {
935
                            TypeElement te = cls.resolve(control);
936
                            if (te != null) {
937
                                Iterable<? extends ExecutableElement> methods = ElementFilter.methodsIn(te.getEnclosedElements());
938
                                for (ExecutableElement method : methods) {
939
                                    if (isMainMethod(method)) {
940
                                        if (isIncluded(cls, control.getClasspathInfo())) {
941
                                            result.add (cls);
942
                                        }
943
                                        break;
944
                                    }
945
                                }
946
                            }
947
                        }
948
                    }
949
                }, false);
950
            } catch (IOException ioe) {
951
                Exceptions.printStackTrace(ioe);
952
                return Collections.<ElementHandle<TypeElement>>emptySet();
953
            }
954
        }
955
        return result;
956
    }
957
958
    private static boolean isIncluded (final ElementHandle<TypeElement> element, final ClasspathInfo cpInfo) {
959
        FileObject fobj = getFile (element,cpInfo);
960
        if (fobj == null) {
961
            //Not source
962
            return true;
963
        }
964
        ClassPath sourcePath = cpInfo.getClassPath(ClasspathInfo.PathKind.SOURCE);
965
        for (ClassPath.Entry e : sourcePath.entries()) {
966
            FileObject root = e.getRoot ();
967
            if (root != null && FileUtil.isParentOf(root,fobj)) {
968
                return e.includes(fobj);
969
            }
970
        }
971
        return true;
972
    }
973
    
974
    private static boolean isCaseSensitive () {
975
        return ! new File ("a").equals (new File ("A"));    //NOI18N
976
    }
977
    
978
    private static String getSourceFileName (String classFileName) {
979
        int index = classFileName.indexOf('$'); //NOI18N
980
        return index == -1 ? classFileName : classFileName.substring(0,index);
981
        }
982
        
983
    /**
984
     * @since 0.24
985
     */
986
    public static WildcardType resolveCapturedType(TypeMirror type) {
987
        if (type instanceof Type.CapturedType) {
988
            return ((Type.CapturedType) type).wildcard;
989
        } else {
990
            return null;
991
        }
992
    }
993
    
994
    // --------------- Helper methods of getFile () -----------------------------
995
    private static ClassPath createClassPath (ClasspathInfo cpInfo, PathKind kind) throws MalformedURLException {
996
	return ClasspathInfoAccessor.getINSTANCE().getCachedClassPath(cpInfo, kind);	
997
    }    
998
    
999
    // --------------- End of getFile () helper methods ------------------------------
1000
1001
    private static final int MAX_LEN = 6;
1002
    /**
1003
     * Utility method for generating method parameter names based on incoming
1004
     * class name when source is unavailable.
1005
     * <p/>
1006
     * This method uses both subjective heuristics to follow common patterns
1007
     * for common JDK classes, acronym creation for bicapitalized names, and
1008
     * vowel and repeated character elision if that fails, to generate
1009
     * readable, programmer-friendly method names.
1010
     *
1011
     * @param typeName The fqn of the parameter class
1012
     * @param used A set of names that have already been used for parameters
1013
     * and should not be reused, to avoid creating uncompilable code
1014
     * @return A programmer-friendly parameter name (i.e. not arg0, arg1...)
1015
     */
1016
    static @NonNull String generateReadableParameterName (@NonNull String typeName, @NonNull Set<String> used) {
1017
        boolean arr = typeName.indexOf ("[") > 0 || typeName.endsWith("..."); //NOI18N
1018
        typeName = trimToSimpleName (typeName);
1019
        String result = typeName.toLowerCase();
1020
        //First, do some common, sane substitutions that are common java parlance
1021
        if ( typeName.endsWith ( "Listener" ) ) { //NOI18N
1022
            result = Character.toLowerCase(typeName.charAt(0)) + "l"; //NOI18N
1023
        } else if ( "Object".equals (typeName)) { //NOI18N
1024
            result = "o"; //NOI18N
1025
        } else if ("Class".equals(typeName)) { //NOI18N
1026
            result = "type"; //NOI18N
1027
        } else if ( "InputStream".equals(typeName)) { //NOI18N
1028
            result = "in"; //NOI18N
1029
        } else if ( "OutputStream".equals(typeName)) {
1030
            result = "out"; //NOI18N
1031
        } else if ( "Runnable".equals(typeName)) {
1032
            result = "r"; //NOI18N
1033
        } else if ( "Lookup".equals(typeName)) {
1034
            result = "lkp"; //NOI18N
1035
        } else if ( typeName.endsWith ( "Stream" )) { //NOI18N
1036
            result = "stream"; //NOI18N
1037
        } else if ( typeName.endsWith ("Writer")) { //NOI18N
1038
            result = "writer"; //NOI18N
1039
        } else if ( typeName.endsWith ("Reader")) { //NOI18N
1040
            result = "reader"; //NOI18N
1041
        } else if ( typeName.endsWith ( "Panel" )) { //NOI18N
1042
            result = "pnl"; //NOI18N
1043
        } else if ( typeName.endsWith ( "Action" )) { //NOI18N
1044
            result = "action"; //NOI18N
1045
        }
1046
        //Now see if we've made a large and unwieldy variable - people
1047
        //usually prefer reasonably short but legible arguments
1048
        if ( result.length () > MAX_LEN ) {
1049
            //See if we can turn, say, NoClassDefFoundError into "ncdfe"
1050
            result = tryToMakeAcronym ( typeName );
1051
            //No luck?  We've probably got one long word like Component or Runnable
1052
            if (result.length() > MAX_LEN) {
1053
                //First, strip out vowels - people easily figure out words
1054
                //missing vowels - common in abbreviations and spam mails
1055
                result = elideVowelsAndRepetitions(result);
1056
                if (result.length() > MAX_LEN) {
1057
                    //Still too long?  Give up and give them a 1 character var name
1058
                    result = new StringBuilder().append(
1059
                            result.charAt(0)).toString().toLowerCase();
1060
                }
1061
            }
1062
        }
1063
        //Make sure we haven't killed everything - if so, use a generic version
1064
        if ( result.trim ().length () == 0 ) {
1065
            result = "value"; //NOI18N
1066
        }
1067
        //If it's an array, pluralize it (english language style - but better than nothing)
1068
        if (arr) {
1069
            result += "s"; //NOI18N
1070
        }
1071
        //Now make sure it's legal;  if not, make it a single letter
1072
        if ( isPrimitiveTypeName ( result ) || !Utilities.isJavaIdentifier ( result ) ) {
1073
            StringBuilder sb = new StringBuilder();
1074
            sb.append (result.charAt(0));
1075
            result = sb.toString();
1076
        }
1077
        //Now make sure we're not duplicating a variable name we already used
1078
        String test = result;
1079
        int revs = 0;
1080
        while ( used.contains ( test ) ) {
1081
            revs++;
1082
            test = result + revs;
1083
        }
1084
        result = test;
1085
        used.add ( result );
1086
        return result;
1087
    }
1088
1089
    /**
1090
     * Trims to the simple class name and removes and generics
1091
     *
1092
     * @param typeName The class name
1093
     * @return A simplified class name
1094
     */
1095
    private static String trimToSimpleName (String typeName) {
1096
        String result = typeName;
1097
        int ix = result.indexOf ("<"); //NOI18N
1098
        if (ix > 0 && ix != typeName.length() - 1) {
1099
            result = typeName.substring(0, ix);
1100
        }
1101
        if (result.endsWith ("...")) { //NOI18N
1102
            result = result.substring (0, result.length() - 3);
1103
        }
1104
        ix = result.lastIndexOf ("$"); //NOI18N
1105
        if (ix > 0 && ix != result.length() - 1) {
1106
            result = result.substring(ix + 1);
1107
        } else {
1108
            ix = result.lastIndexOf("."); //NOI18N
1109
            if (ix > 0 && ix != result.length() - 1) {
1110
                result = result.substring(ix + 1);
1111
            }
1112
        }
1113
        ix = result.indexOf ( "[" ); //NOI18N
1114
        if ( ix > 0 ) {
1115
            result = result.substring ( 0, ix );
1116
        }
1117
        return result;
1118
    }
1119
1120
    /**
1121
     * Removes vowels and repeated letters.  This is used to generate names
1122
     * where the class name a single long word - e.g. abbreviate
1123
     * Runnable to rnbl
1124
     * @param name The name
1125
     * @return A shortened version of it
1126
     */
1127
    private static String elideVowelsAndRepetitions (String name) {
1128
        char[] chars = name.toCharArray();
1129
        StringBuilder sb = new StringBuilder();
1130
        char last = 0;
1131
        char lastUsed = 0;
1132
        for (int i = 0; i < chars.length; i++) {
1133
            char c = chars[i];
1134
            if (Character.isDigit(c)) {
1135
                continue;
1136
            }
1137
            if (i == 0 || Character.isUpperCase(c)) {
1138
                if (lastUsed != c) {
1139
                    sb.append (c);
1140
                    lastUsed = c;
1141
                }
1142
            } else if (c != last && !isVowel(c)) {
1143
                if (lastUsed != c) {
1144
                    sb.append (c);
1145
                    lastUsed = c;
1146
                }
1147
            }
1148
            last = c;
1149
        }
1150
        return sb.toString();
1151
    }
1152
1153
    private static boolean isVowel(char c) {
1154
        return Arrays.binarySearch(VOWELS, c) >= 0;
1155
    }
1156
1157
    /**
1158
     * Vowels in various indo-european-based languages
1159
     */
1160
    private static char[] VOWELS = new char[] {
1161
    //IMPORTANT:  This array is sorted.  If you add to it,
1162
    //add in the correct place or Arrays.binarySearch will break on it
1163
    '\u0061', '\u0065', '\u0069', '\u006f', '\u0075', '\u0079', '\u00e9', '\u00ea',  //NOI18N
1164
    '\u00e8', '\u00e1', '\u00e2', '\u00e6', '\u00e0', '\u03b1', '\u00e3',  //NOI18N
1165
    '\u00e5', '\u00e4', '\u00eb', '\u00f3', '\u00f4', '\u0153', '\u00f2',  //NOI18N
1166
    '\u03bf', '\u00f5', '\u00f6', '\u00ed', '\u00ee', '\u00ec', '\u03b9',  //NOI18N
1167
    '\u00ef', '\u00fa', '\u00fb', '\u00f9', '\u03d2', '\u03c5', '\u00fc',  //NOI18N
1168
    '\u0430', '\u043e', '\u044f', '\u0438', '\u0439', '\u0435', '\u044b',  //NOI18N
1169
    '\u044d', '\u0443', '\u044e', };
1170
1171
    //PENDING:  The below would be much prettier;  whether it survives
1172
    //cross-platform encoding issues in hg is another question;  the hg diff generated
1173
    //was incorrect
1174
/*
1175
    'a', 'e', 'i', 'o', 'u', 'y', 'à', 'á', //NOI18N
1176
    'â', 'ã', 'ä', 'å', 'æ', 'è', 'é', //NOI18N
1177
    'ê', 'ë', 'ì', 'í', 'î', 'ï', 'ò', //NOI18N
1178
    'ó', 'ô', 'õ', 'ö', 'ù', 'ú', 'û', //NOI18N
1179
    'ü', 'œ', 'α', 'ι', 'ο', 'υ', 'ϒ', //NOI18N
1180
    'а', 'е', 'и', 'й', 'о', 'у', 'ы', //NOI18N
1181
    'э', 'ю', 'я'}; //NOI18N
1182
*/
1183
    /**
1184
     * Determine if a string matches a java primitive type.  Used in generating reasonable variable names.
1185
     */
1186
    private static boolean isPrimitiveTypeName (String typeName) {
1187
        return (
1188
                //Whoa, ascii art!
1189
                "void".equals ( typeName ) || //NOI18N
1190
                "int".equals ( typeName ) || //NOI18N
1191
                "long".equals ( typeName ) || //NOI18N
1192
                "float".equals ( typeName ) || //NOI18N
1193
                "double".equals ( typeName ) || //NOI18N
1194
                "short".equals ( typeName ) || //NOI18N
1195
                "char".equals ( typeName ) || //NOI18N
1196
                "boolean".equals ( typeName ) ); //NOI18N
1197
    }
1198
1199
    /**
1200
     * Try to create an acronym-style variable name from a string - i.e.,
1201
     * "JavaDataObject" becomes "jdo".
1202
     */
1203
    private static String tryToMakeAcronym (String s) {
1204
        char[] c = s.toCharArray ();
1205
        StringBuilder sb = new StringBuilder ();
1206
        for ( int i = 0; i < c.length; i++ ) {
1207
            if ( Character.isUpperCase (c[i])) {
1208
                sb.append ( c[ i ] );
1209
            }
1210
        }
1211
        if ( sb.length () > 1 ) {
1212
            return sb.toString ().toLowerCase ();
1213
        } else {
1214
            return s.toLowerCase();
1215
        }
1216
    }
1217
}

Return to bug 179508