KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > corext > codemanipulation > ImportsStructure


1 /*******************************************************************************
2  * Copyright (c) 2000, 2005 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.corext.codemanipulation;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.HashMap JavaDoc;
15 import java.util.HashSet JavaDoc;
16 import java.util.List JavaDoc;
17 import java.util.Set JavaDoc;
18
19 import org.eclipse.text.edits.DeleteEdit;
20 import org.eclipse.text.edits.InsertEdit;
21 import org.eclipse.text.edits.MalformedTreeException;
22 import org.eclipse.text.edits.MultiTextEdit;
23 import org.eclipse.text.edits.TextEdit;
24
25 import org.eclipse.core.runtime.CoreException;
26 import org.eclipse.core.runtime.IPath;
27 import org.eclipse.core.runtime.IProgressMonitor;
28 import org.eclipse.core.runtime.IStatus;
29 import org.eclipse.core.runtime.NullProgressMonitor;
30 import org.eclipse.core.runtime.SubProgressMonitor;
31
32 import org.eclipse.core.filebuffers.FileBuffers;
33 import org.eclipse.core.filebuffers.ITextFileBufferManager;
34
35 import org.eclipse.core.resources.IFile;
36
37 import org.eclipse.jface.text.BadLocationException;
38 import org.eclipse.jface.text.Document;
39 import org.eclipse.jface.text.DocumentRewriteSession;
40 import org.eclipse.jface.text.DocumentRewriteSessionType;
41 import org.eclipse.jface.text.IDocument;
42 import org.eclipse.jface.text.IDocumentExtension4;
43 import org.eclipse.jface.text.IRegion;
44 import org.eclipse.jface.text.Region;
45 import org.eclipse.jface.text.TextUtilities;
46
47 import org.eclipse.jdt.core.Flags;
48 import org.eclipse.jdt.core.ICompilationUnit;
49 import org.eclipse.jdt.core.IImportContainer;
50 import org.eclipse.jdt.core.IImportDeclaration;
51 import org.eclipse.jdt.core.IJavaElement;
52 import org.eclipse.jdt.core.IPackageDeclaration;
53 import org.eclipse.jdt.core.ISourceRange;
54 import org.eclipse.jdt.core.IType;
55 import org.eclipse.jdt.core.JavaModelException;
56 import org.eclipse.jdt.core.Signature;
57 import org.eclipse.jdt.core.ToolFactory;
58 import org.eclipse.jdt.core.dom.AST;
59 import org.eclipse.jdt.core.dom.IBinding;
60 import org.eclipse.jdt.core.dom.IMethodBinding;
61 import org.eclipse.jdt.core.dom.ITypeBinding;
62 import org.eclipse.jdt.core.dom.IVariableBinding;
63 import org.eclipse.jdt.core.dom.ParameterizedType;
64 import org.eclipse.jdt.core.dom.PrimitiveType;
65 import org.eclipse.jdt.core.dom.Type;
66 import org.eclipse.jdt.core.dom.WildcardType;
67 import org.eclipse.jdt.core.formatter.CodeFormatter;
68 import org.eclipse.jdt.core.search.IJavaSearchConstants;
69 import org.eclipse.jdt.core.search.IJavaSearchScope;
70 import org.eclipse.jdt.core.search.SearchEngine;
71 import org.eclipse.jdt.core.search.TypeNameRequestor;
72
73 import org.eclipse.jdt.internal.corext.ValidateEditException;
74 import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
75 import org.eclipse.jdt.internal.corext.dom.Bindings;
76 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
77 import org.eclipse.jdt.internal.corext.util.Resources;
78 import org.eclipse.jdt.internal.corext.util.Strings;
79
80 import org.eclipse.jdt.internal.ui.JavaPlugin;
81 import org.eclipse.jdt.internal.ui.JavaUIStatus;
82
83 /**
84  * Created on a Compilation unit, the ImportsStructure allows to add
85  * Import Declarations that are added next to the existing import that
86  * has the best match.
87  */

88 public final class ImportsStructure implements IImportsStructure {
89     
90     private ICompilationUnit fCompilationUnit;
91     private ArrayList JavaDoc fPackageEntries;
92     
93     private int fImportOnDemandThreshold;
94     
95     private boolean fFilterImplicitImports;
96     private boolean fFindAmbiguousImports;
97     
98     private List JavaDoc fImportsCreated;
99     private List JavaDoc fStaticImportsCreated;
100     private boolean fHasChanges= false;
101     private IRegion fReplaceRange;
102     
103     private static final String JavaDoc JAVA_LANG= "java.lang"; //$NON-NLS-1$
104

105     /**
106      * Creates an ImportsStructure for a compilation unit. New imports
107      * are added next to the existing import that is matching best.
108      * @param cu The compilation unit
109      * @param preferenceOrder Defines the preferred order of imports.
110      * @param importThreshold Defines the number of imports in a package needed to introduce a
111      * import on demand instead (e.g. java.util.*).
112      * @param restoreExistingImports If set, existing imports are kept. No imports are deleted, only new added.
113      * @throws CoreException
114      */

115     public ImportsStructure(ICompilationUnit cu, String JavaDoc[] preferenceOrder, int importThreshold, boolean restoreExistingImports) throws CoreException {
116         fCompilationUnit= cu;
117         JavaModelUtil.reconcile(cu);
118     
119         IImportContainer container= cu.getImportContainer();
120         
121         fImportOnDemandThreshold= importThreshold;
122         fFilterImplicitImports= true;
123         fFindAmbiguousImports= true; //!restoreExistingImports;
124

125         fPackageEntries= new ArrayList JavaDoc(20);
126         fImportsCreated= null; // initialized on 'create'
127
fStaticImportsCreated= null;
128         
129         IProgressMonitor monitor= new NullProgressMonitor();
130         IDocument document= null;
131         try {
132             document= aquireDocument(monitor);
133             fReplaceRange= evaluateReplaceRange(document);
134             if (restoreExistingImports && container.exists()) {
135                 addExistingImports(document, cu.getImports(), fReplaceRange);
136             }
137         } catch (BadLocationException e) {
138             throw new CoreException(JavaUIStatus.createError(IStatus.ERROR, e));
139         } finally {
140             if (document != null) {
141                 releaseDocument(document, monitor);
142             }
143         }
144         PackageEntry[] order= new PackageEntry[preferenceOrder.length];
145         for (int i= 0; i < order.length; i++) {
146             String JavaDoc curr= preferenceOrder[i];
147             if (curr.length() > 0 && curr.charAt(0) == '#') {
148                 curr= curr.substring(1);
149                 order[i]= new PackageEntry(curr, curr, true); // static import group
150
} else {
151                 order[i]= new PackageEntry(curr, curr, false); // normal import group
152
}
153         }
154         
155         addPreferenceOrderHolders(order);
156         
157         fHasChanges= false;
158     }
159     
160     private void addPreferenceOrderHolders(PackageEntry[] preferenceOrder) {
161         if (fPackageEntries.isEmpty()) {
162             // all new: copy the elements
163
for (int i= 0; i < preferenceOrder.length; i++) {
164                 fPackageEntries.add(preferenceOrder[i]);
165             }
166         } else {
167             // match the preference order entries to existing imports
168
// entries not found are appended after the last successfully matched entry
169

170             PackageEntry[] lastAssigned= new PackageEntry[preferenceOrder.length];
171             
172             // find an existing package entry that matches most
173
for (int k= 0; k < fPackageEntries.size(); k++) {
174                 PackageEntry entry= (PackageEntry) fPackageEntries.get(k);
175                 if (!entry.isComment()) {
176                     String JavaDoc currName= entry.getName();
177                     int currNameLen= currName.length();
178                     int bestGroupIndex= -1;
179                     int bestGroupLen= -1;
180                     for (int i= 0; i < preferenceOrder.length; i++) {
181                         boolean currPrevStatic= preferenceOrder[i].isStatic();
182                         if (currPrevStatic == entry.isStatic()) {
183                             String JavaDoc currPrefEntry= preferenceOrder[i].getName();
184                             int currPrefLen= currPrefEntry.length();
185                             if (currName.startsWith(currPrefEntry) && currPrefLen >= bestGroupLen) {
186                                 if (currPrefLen == currNameLen || currName.charAt(currPrefLen) == '.') {
187                                     if (bestGroupIndex == -1 || currPrefLen > bestGroupLen) {
188                                         bestGroupLen= currPrefLen;
189                                         bestGroupIndex= i;
190                                     }
191                                 }
192                             }
193                         }
194                     }
195                     if (bestGroupIndex != -1) {
196                         entry.setGroupID(preferenceOrder[bestGroupIndex].getName());
197                         lastAssigned[bestGroupIndex]= entry; // remember last entry
198
}
199                 }
200             }
201             // fill in not-assigned categories, keep partial order
202
int currAppendIndex= 0;
203             for (int i= 0; i < lastAssigned.length; i++) {
204                 PackageEntry entry= lastAssigned[i];
205                 if (entry == null) {
206                     PackageEntry newEntry= preferenceOrder[i];
207                     if (currAppendIndex == 0 && !newEntry.isStatic()) {
208                         currAppendIndex= getIndexAfterStatics();
209                     }
210                     fPackageEntries.add(currAppendIndex, newEntry);
211                     currAppendIndex++;
212                 } else {
213                     currAppendIndex= fPackageEntries.indexOf(entry) + 1;
214                 }
215             }
216         }
217     }
218
219     
220     private void addExistingImports(IDocument document, IImportDeclaration[] decls, IRegion replaceRange) throws JavaModelException, BadLocationException {
221         if (decls.length == 0) {
222             return;
223         }
224         PackageEntry currPackage= null;
225             
226         IImportDeclaration curr= decls[0];
227         ISourceRange sourceRange= curr.getSourceRange();
228         int currOffset= sourceRange.getOffset();
229         int currLength= sourceRange.getLength();
230         int currEndLine= document.getLineOfOffset(currOffset + currLength);
231             
232         for (int i= 1; i < decls.length; i++) {
233             String JavaDoc name= curr.getElementName();
234             boolean isStatic= Flags.isStatic(curr.getFlags());
235                 
236             String JavaDoc packName= Signature.getQualifier(name);
237             if (currPackage == null || currPackage.compareTo(packName, isStatic) != 0) {
238                 currPackage= new PackageEntry(packName, null, isStatic);
239                 fPackageEntries.add(currPackage);
240             }
241
242             IImportDeclaration next= decls[i];
243             sourceRange= next.getSourceRange();
244             int nextOffset= sourceRange.getOffset();
245             int nextLength= sourceRange.getLength();
246             int nextOffsetLine= document.getLineOfOffset(nextOffset);
247
248             // if next import is on a different line, modify the end position to the next line begin offset
249
if (currEndLine < nextOffsetLine) {
250                 currEndLine++;
251                 nextOffset= document.getLineInformation(currEndLine).getOffset();
252             }
253             currPackage.add(new ImportDeclEntry(name, isStatic, new Region(currOffset, nextOffset - currOffset)));
254             currOffset= nextOffset;
255             curr= next;
256                 
257             // add a comment entry for spacing between imports
258
if (currEndLine < nextOffsetLine) {
259                 nextOffset= document.getLineInformation(nextOffsetLine).getOffset();
260                 
261                 currPackage= new PackageEntry(); // create a comment package entry for this
262
fPackageEntries.add(currPackage);
263                 currPackage.add(new ImportDeclEntry(null, false, new Region(currOffset, nextOffset - currOffset)));
264                     
265                 currOffset= nextOffset;
266             }
267             currEndLine= document.getLineOfOffset(nextOffset + nextLength);
268         }
269
270         String JavaDoc name= curr.getElementName();
271         boolean isStatic= Flags.isStatic(curr.getFlags());
272         String JavaDoc packName= Signature.getQualifier(name);
273         if (currPackage == null || currPackage.compareTo(packName, isStatic) != 0) {
274             currPackage= new PackageEntry(packName, null, isStatic);
275             fPackageEntries.add(currPackage);
276         }
277         ISourceRange range= curr.getSourceRange();
278         int length= replaceRange.getOffset() + replaceRange.getLength() - range.getOffset();
279         currPackage.add(new ImportDeclEntry(name, isStatic, new Region(range.getOffset(), length)));
280     }
281         
282     /**
283      * @return Returns the compilation unit of this import structure.
284      */

285     public ICompilationUnit getCompilationUnit() {
286         return fCompilationUnit;
287     }
288     
289     /**
290      * Sets that implicit imports (types in default package, cu- package and
291      * 'java.lang') should not be created. Note that this is a heuristic filter and can
292      * lead to missing imports, e.g. in cases where a type is forced to be specified
293      * due to a name conflict.
294      * By default, the filter is enabled.
295      * @param filterImplicitImports The filterImplicitImports to set
296      */

297     public void setFilterImplicitImports(boolean filterImplicitImports) {
298         fFilterImplicitImports= filterImplicitImports;
299     }
300     
301     /**
302      * When set searches for imports that can not be folded into on-demand
303      * imports but must be specified explicitly
304      * @param findAmbiguousImports The new value
305      */

306     public void setFindAmbiguousImports(boolean findAmbiguousImports) {
307         fFindAmbiguousImports= findAmbiguousImports;
308     }
309             
310     private static class PackageMatcher {
311         private String JavaDoc fNewName;
312         private String JavaDoc fBestName;
313         private int fBestMatchLen;
314         
315         public PackageMatcher() {
316         }
317         
318         public void initialize(String JavaDoc newName, String JavaDoc bestName) {
319             fNewName= newName;
320             fBestName= bestName;
321             fBestMatchLen= getCommonPrefixLength(bestName, fNewName);
322         }
323         
324         public boolean isBetterMatch(String JavaDoc currName, boolean preferCurr) {
325             boolean isBetter;
326             int currMatchLen= getCommonPrefixLength(currName, fNewName);
327             int matchDiff= currMatchLen - fBestMatchLen;
328             if (matchDiff == 0) {
329                 if (currMatchLen == fNewName.length() && currMatchLen == currName.length() && currMatchLen == fBestName.length()) {
330                     // duplicate entry and complete match
331
isBetter= preferCurr;
332                 } else {
333                     isBetter= sameMatchLenTest(currName);
334                 }
335             } else {
336                 isBetter= (matchDiff > 0); // curr has longer match
337
}
338             if (isBetter) {
339                 fBestName= currName;
340                 fBestMatchLen= currMatchLen;
341             }
342             return isBetter;
343         }
344                 
345         private boolean sameMatchLenTest(String JavaDoc currName) {
346             int matchLen= fBestMatchLen;
347             // known: bestName and currName differ from newName at position 'matchLen'
348
// currName and bestName dont have to differ at position 'matchLen'
349

350             // determine the order and return true if currName is closer to newName
351
char newChar= getCharAt(fNewName, matchLen);
352             char currChar= getCharAt(currName, matchLen);
353             char bestChar= getCharAt(fBestName, matchLen);
354
355             if (newChar < currChar) {
356                 if (bestChar < newChar) { // b < n < c
357
return (currChar - newChar) < (newChar - bestChar); // -> (c - n) < (n - b)
358
} else { // n < b && n < c
359
if (currChar == bestChar) { // longer match between curr and best
360
return false; // keep curr and best together, new should be before both
361
} else {
362                         return currChar < bestChar; // -> (c < b)
363
}
364                 }
365             } else {
366                 if (bestChar > newChar) { // c < n < b
367
return (newChar - currChar) < (bestChar - newChar); // -> (n - c) < (b - n)
368
} else { // n > b && n > c
369
if (currChar == bestChar) { // longer match between curr and best
370
return true; // keep curr and best together, new should be ahead of both
371
} else {
372                         return currChar > bestChar; // -> (c > b)
373
}
374             }
375         }
376         }
377             
378     }
379
380     private static int getCommonPrefixLength(String JavaDoc s, String JavaDoc t) {
381         int len= Math.min(s.length(), t.length());
382         for (int i= 0; i < len; i++) {
383             if (s.charAt(i) != t.charAt(i)) {
384                 return i;
385             }
386         }
387         return len;
388     }
389
390     private static char getCharAt(String JavaDoc str, int index) {
391         if (str.length() > index) {
392             return str.charAt(index);
393         }
394         return 0;
395     }
396     
397     private PackageEntry findBestMatch(String JavaDoc newName, boolean isStatic) {
398         if (fPackageEntries.isEmpty()) {
399             return null;
400         }
401         String JavaDoc groupId= null;
402         int longestPrefix= -1;
403         // find the matching group
404
for (int i= 0; i < fPackageEntries.size(); i++) {
405             PackageEntry curr= (PackageEntry) fPackageEntries.get(i);
406             if (isStatic == curr.isStatic()) {
407                 String JavaDoc currGroup= curr.getGroupID();
408                 if (currGroup != null && newName.startsWith(currGroup)) {
409                     int prefixLen= currGroup.length();
410                     if (prefixLen == newName.length()) {
411                         return curr; // perfect fit, use entry
412
}
413                     if ((newName.charAt(prefixLen) == '.') && prefixLen > longestPrefix) {
414                         longestPrefix= prefixLen;
415                         groupId= currGroup;
416                     }
417                 }
418             }
419         }
420         PackageEntry bestMatch= null;
421         PackageMatcher matcher= new PackageMatcher();
422         matcher.initialize(newName, ""); //$NON-NLS-1$
423
for (int i= 0; i < fPackageEntries.size(); i++) { // find the best match with the same group
424
PackageEntry curr= (PackageEntry) fPackageEntries.get(i);
425             if (!curr.isComment() && curr.isStatic() == isStatic) {
426                 if (groupId == null || groupId.equals(curr.getGroupID())) {
427                     boolean preferrCurr= (bestMatch == null) || (curr.getNumberOfImports() > bestMatch.getNumberOfImports());
428                     if (matcher.isBetterMatch(curr.getName(), preferrCurr)) {
429                         bestMatch= curr;
430                     }
431                 }
432             }
433         }
434         return bestMatch;
435     }
436         
437     public static boolean isImplicitImport(String JavaDoc qualifier, ICompilationUnit cu) {
438         if (JAVA_LANG.equals(qualifier)) { //$NON-NLS-1$
439
return true;
440         }
441         String JavaDoc packageName= cu.getParent().getElementName();
442         if (qualifier.equals(packageName)) {
443             return true;
444         }
445         String JavaDoc mainTypeName= JavaModelUtil.concatenateName(packageName, Signature.getQualifier(cu.getElementName()));
446         return qualifier.equals(mainTypeName);
447     }
448     
449     /**
450      * Adds a new import declaration that is sorted in the structure using
451      * a best match algorithm. If an import already exists, the import is
452      * not added.
453      * @param binding The type binding of the type to be added
454      * @param ast The ast to create the type for
455      * @return Returns the a new AST node that is either simple if the import was successful or
456      * fully qualified type name if the import could not be added due to a conflict.
457      */

458     public Type addImport(ITypeBinding binding, AST ast) {
459         if (binding.isPrimitive()) {
460             return ast.newPrimitiveType(PrimitiveType.toCode(binding.getName()));
461         }
462         
463         ITypeBinding normalizedBinding= Bindings.normalizeTypeBinding(binding);
464         if (normalizedBinding == null) {
465             return ast.newSimpleType(ast.newSimpleName("invalid")); //$NON-NLS-1$
466
}
467         
468         if (normalizedBinding.isTypeVariable()) {
469             // no import
470
return ast.newSimpleType(ast.newSimpleName(binding.getName()));
471         }
472         if (normalizedBinding.isWildcardType()) {
473             WildcardType wcType= ast.newWildcardType();
474             ITypeBinding bound= normalizedBinding.getBound();
475             if (bound != null && !bound.isWildcardType() && !bound.isCapture()) { // bug 96942
476
Type boundType= addImport(bound, ast);
477                 wcType.setBound(boundType, normalizedBinding.isUpperbound());
478             }
479             return wcType;
480         }
481         
482         if (normalizedBinding.isArray()) {
483             Type elementType= addImport(normalizedBinding.getElementType(), ast);
484             return ast.newArrayType(elementType, normalizedBinding.getDimensions());
485         }
486         
487         String JavaDoc qualifiedName= Bindings.getRawQualifiedName(normalizedBinding);
488         if (qualifiedName.length() > 0) {
489             String JavaDoc res= internalAddImport(qualifiedName);
490             
491             ITypeBinding[] typeArguments= normalizedBinding.getTypeArguments();
492             if (typeArguments.length > 0) {
493                 Type erasureType= ast.newSimpleType(ASTNodeFactory.newName(ast,res));
494                 ParameterizedType paramType= ast.newParameterizedType(erasureType);
495                 List JavaDoc arguments= paramType.typeArguments();
496                 for (int i= 0; i < typeArguments.length; i++) {
497                     arguments.add(addImport(typeArguments[i], ast));
498                 }
499                 return paramType;
500             }
501             return ast.newSimpleType(ASTNodeFactory.newName(ast, res));
502         }
503         return ast.newSimpleType(ASTNodeFactory.newName(ast, Bindings.getRawName(normalizedBinding)));
504     }
505     
506     /**
507      * Adds a new import declaration that is sorted in the structure using
508      * a best match algorithm. If an import already exists, the import is
509      * not added.
510      * @param typeSig The type in signature notation
511      * @param ast The ast to create the type for
512      * @return Returns the a new AST node that is either simple if the import was successful or
513      * fully qualified type name if the import could not be added due to a conflict.
514      */

515     public Type addImportFromSignature(String JavaDoc typeSig, AST ast) {
516         if (typeSig == null || typeSig.length() == 0) {
517             throw new IllegalArgumentException JavaDoc("Invalid type signature: empty or null"); //$NON-NLS-1$
518
}
519         int sigKind= Signature.getTypeSignatureKind(typeSig);
520         switch (sigKind) {
521             case Signature.BASE_TYPE_SIGNATURE:
522                 return ast.newPrimitiveType(PrimitiveType.toCode(Signature.toString(typeSig)));
523             case Signature.ARRAY_TYPE_SIGNATURE:
524                 Type elementType= addImportFromSignature(Signature.getElementType(typeSig), ast);
525                 return ast.newArrayType(elementType, Signature.getArrayCount(typeSig));
526             case Signature.CLASS_TYPE_SIGNATURE:
527                 String JavaDoc erasureSig= Signature.getTypeErasure(typeSig);
528
529                 String JavaDoc erasureName= Signature.toString(erasureSig);
530                 if (erasureSig.charAt(0) == Signature.C_RESOLVED) {
531                     erasureName= internalAddImport(erasureName);
532                 }
533                 Type baseType= ast.newSimpleType(ASTNodeFactory.newName(ast, erasureName));
534                 String JavaDoc[] typeArguments= Signature.getTypeArguments(typeSig);
535                 if (typeArguments.length > 0) {
536                     ParameterizedType type= ast.newParameterizedType(baseType);
537                     List JavaDoc argNodes= type.typeArguments();
538                     for (int i= 0; i < typeArguments.length; i++) {
539                         argNodes.add(addImportFromSignature(typeArguments[i], ast));
540                     }
541                     return type;
542                 }
543                 return baseType;
544             case Signature.TYPE_VARIABLE_SIGNATURE:
545                 return ast.newSimpleType(ast.newSimpleName(Signature.toString(typeSig)));
546             case Signature.WILDCARD_TYPE_SIGNATURE:
547                 WildcardType wildcardType= ast.newWildcardType();
548                 char ch= typeSig.charAt(0);
549                 if (ch != Signature.C_STAR) {
550                     Type bound= addImportFromSignature(typeSig.substring(1), ast);
551                     wildcardType.setBound(bound, ch == Signature.C_EXTENDS);
552                 }
553                 return wildcardType;
554             case Signature.CAPTURE_TYPE_SIGNATURE:
555                 return addImportFromSignature(typeSig.substring(1), ast);
556             default:
557                 JavaPlugin.logErrorMessage("Unknown type signature kind: " + typeSig); //$NON-NLS-1$
558
}
559         return ast.newSimpleType(ast.newSimpleName("invalid")); //$NON-NLS-1$
560
}
561     
562
563     /**
564      * Adds a new import declaration that is sorted in the structure using
565      * a best match algorithm. If an import already exists, the import is
566      * not added.
567      * @param binding The type binding of the type to be added
568      * @return Returns the name to use in the code: Simple name if the import
569      * was added, fully qualified type name if the import could not be added due
570      * to a conflict.
571      */

572     public String JavaDoc addImport(ITypeBinding binding) {
573         
574         if (binding.isPrimitive() || binding.isTypeVariable()) {
575             return binding.getName();
576         }
577         
578         ITypeBinding normalizedBinding= Bindings.normalizeTypeBinding(binding);
579         if (normalizedBinding == null) {
580             return "invalid"; //$NON-NLS-1$
581
}
582         if (normalizedBinding.isWildcardType()) {
583             StringBuffer JavaDoc res= new StringBuffer JavaDoc("?"); //$NON-NLS-1$
584
ITypeBinding bound= normalizedBinding.getBound();
585             if (bound != null && !bound.isWildcardType() && !bound.isCapture()) { // bug 95942
586
if (normalizedBinding.isUpperbound()) {
587                     res.append(" extends "); //$NON-NLS-1$
588
} else {
589                     res.append(" super "); //$NON-NLS-1$
590
}
591                 res.append(addImport(bound));
592             }
593             return res.toString();
594         }
595         
596         if (normalizedBinding.isArray()) {
597             StringBuffer JavaDoc res= new StringBuffer JavaDoc(addImport(normalizedBinding.getElementType()));
598             for (int i= normalizedBinding.getDimensions(); i > 0; i--) {
599                 res.append("[]"); //$NON-NLS-1$
600
}
601             return res.toString();
602         }
603         
604
605         
606         String JavaDoc qualifiedName= Bindings.getRawQualifiedName(normalizedBinding);
607         if (qualifiedName.length() > 0) {
608             String JavaDoc str= internalAddImport(qualifiedName);
609             
610             ITypeBinding[] typeArguments= normalizedBinding.getTypeArguments();
611             if (typeArguments.length > 0) {
612                 StringBuffer JavaDoc res= new StringBuffer JavaDoc(str);
613                 res.append('<');
614                 for (int i= 0; i < typeArguments.length; i++) {
615                     if (i > 0) {
616                         res.append(','); //$NON-NLS-1$
617
}
618                     res.append(addImport(typeArguments[i]));
619                 }
620                 res.append('>');
621                 return res.toString();
622             }
623             return str;
624         }
625         return Bindings.getRawName(normalizedBinding);
626     }
627         
628     /**
629      * Adds a new import declaration that is sorted in the structure using
630      * a best match algorithm. If an import already exists, the import is
631      * not added.
632      * @param qualifiedTypeName The fully qualified name of the type to import
633      * @return Returns either the simple type name if the import was successful or else the qualified type name
634      */

635     public String JavaDoc addImport(String JavaDoc qualifiedTypeName) {
636         int angleBracketOffset= qualifiedTypeName.indexOf('<');
637         if (angleBracketOffset != -1) {
638             return internalAddImport(qualifiedTypeName.substring(0, angleBracketOffset)) + qualifiedTypeName.substring(angleBracketOffset);
639         }
640         int bracketOffset= qualifiedTypeName.indexOf('[');
641         if (bracketOffset != -1) {
642             return internalAddImport(qualifiedTypeName.substring(0, bracketOffset)) + qualifiedTypeName.substring(bracketOffset);
643         }
644         return internalAddImport(qualifiedTypeName);
645     }
646     
647     
648     public String JavaDoc addStaticImport(IBinding binding) {
649         if (binding instanceof IVariableBinding) {
650             ITypeBinding declaringType= ((IVariableBinding) binding).getDeclaringClass();
651             return addStaticImport(Bindings.getRawQualifiedName(declaringType), binding.getName(), true);
652         } else if (binding instanceof IMethodBinding) {
653             ITypeBinding declaringType= ((IMethodBinding) binding).getDeclaringClass();
654             return addStaticImport(Bindings.getRawQualifiedName(declaringType), binding.getName(), false);
655         }
656         return binding.getName();
657     }
658     
659     /**
660      * Adds a new static import declaration that is sorted in the structure using
661      * a best match algorithm. If an import already exists, the import is
662      * not added.
663      * @param declaringTypeName The qualified name of the static's member declaring type
664      * @return Returns either the simple type name if the import was successful or else the qualified type name
665      */

666     public String JavaDoc addStaticImport(String JavaDoc declaringTypeName, String JavaDoc simpleName, boolean isField) {
667         String JavaDoc containerName= Signature.getQualifier(declaringTypeName);
668         String JavaDoc fullName= declaringTypeName + '.' + simpleName;
669         
670         if (containerName.length() == 0) {
671             return declaringTypeName + '.' + simpleName;
672         }
673         if (!"*".equals(simpleName)) { //$NON-NLS-1$
674
if (isField) {
675                 String JavaDoc existing= findStaticImport(null, simpleName);
676                 if (existing != null) {
677                     if (existing.equals(fullName)) {
678                         return simpleName;
679                     } else {
680                         return fullName;
681                     }
682                 }
683             } else {
684                 String JavaDoc existing= findStaticImport(declaringTypeName, simpleName);
685                 if (existing != null) {
686                     return simpleName;
687                 }
688             }
689         }
690         ImportDeclEntry decl= new ImportDeclEntry(fullName, true, null);
691         
692         sortIn(declaringTypeName, decl, true);
693         return simpleName;
694     }
695     
696     private String JavaDoc internalAddImport(String JavaDoc fullTypeName) {
697         int idx= fullTypeName.lastIndexOf('.');
698         String JavaDoc typeContainerName, typeName;
699         if (idx != -1) {
700             typeContainerName= fullTypeName.substring(0, idx);
701             typeName= fullTypeName.substring(idx + 1);
702         } else {
703             typeContainerName= ""; //$NON-NLS-1$
704
typeName= fullTypeName;
705         }
706         
707         if (typeContainerName.length() == 0 && PrimitiveType.toCode(typeName) != null) {
708             return fullTypeName;
709         }
710         
711         if (!"*".equals(typeName)) { //$NON-NLS-1$
712
String JavaDoc topLevelTypeName= Signature.getQualifier(fCompilationUnit.getElementName());
713             
714             if (typeName.equals(topLevelTypeName)) {
715                 if (!typeContainerName.equals(fCompilationUnit.getParent().getElementName())) {
716                     return fullTypeName;
717                 } else {
718                     return typeName;
719                 }
720             }
721             String JavaDoc existing= findImport(typeName);
722             if (existing != null) {
723                 if (fullTypeName.equals(existing)) {
724                     return typeName;
725                 } else {
726                     return fullTypeName;
727                 }
728             }
729         }
730         
731         ImportDeclEntry decl= new ImportDeclEntry(fullTypeName, false, null);
732             
733         sortIn(typeContainerName, decl, false);
734         return typeName;
735     }
736     
737     private int getIndexAfterStatics() {
738         for (int i= 0; i < fPackageEntries.size(); i++) {
739             if (!((PackageEntry) fPackageEntries.get(i)).isStatic()) {
740                 return i;
741             }
742         }
743         return fPackageEntries.size();
744     }
745     
746     
747     private void sortIn(String JavaDoc typeContainerName, ImportDeclEntry decl, boolean isStatic) {
748         PackageEntry bestMatch= findBestMatch(typeContainerName, isStatic);
749         if (bestMatch == null) {
750             PackageEntry packEntry= new PackageEntry(typeContainerName, null, isStatic);
751             packEntry.add(decl);
752             int insertPos= packEntry.isStatic() ? 0 : getIndexAfterStatics();
753             fPackageEntries.add(insertPos, packEntry);
754         } else {
755             int cmp= typeContainerName.compareTo(bestMatch.getName());
756             if (cmp == 0) {
757                 bestMatch.sortIn(decl);
758             } else {
759                 // create a new package entry
760
String JavaDoc group= bestMatch.getGroupID();
761                 if (group != null) {
762                     if (!typeContainerName.startsWith(group)) {
763                         group= null;
764                     }
765                 }
766                 PackageEntry packEntry= new PackageEntry(typeContainerName, group, isStatic);
767                 packEntry.add(decl);
768                 int index= fPackageEntries.indexOf(bestMatch);
769                 if (cmp < 0) { // insert ahead of best match
770
fPackageEntries.add(index, packEntry);
771                 } else { // insert after best match
772
fPackageEntries.add(index + 1, packEntry);
773                 }
774             }
775         }
776         fHasChanges= true;
777     }
778
779     /**
780      * Removes an import from the structure.
781      * @param qualifiedName The qualified type name to remove from the imports
782      * @return Returns <code>true</code> if the import was found and removed
783      */

784     public boolean removeImport(String JavaDoc qualifiedName) {
785         String JavaDoc typeContainerName= Signature.getQualifier(qualifiedName);
786         int bracketOffset= qualifiedName.indexOf('[');
787         if (bracketOffset != -1) {
788             qualifiedName= qualifiedName.substring(0, bracketOffset);
789         }
790         
791         int nPackages= fPackageEntries.size();
792         for (int i= 0; i < nPackages; i++) {
793             PackageEntry entry= (PackageEntry) fPackageEntries.get(i);
794             if (entry.compareTo(typeContainerName, false) == 0) {
795                 if (entry.remove(qualifiedName, false)) {
796                     fHasChanges= true;
797                     return true;
798                 }
799             }
800         }
801         return false;
802     }
803     
804     /**
805      * Removes an import from the structure.
806      * @param qualifiedName The qualified member name to remove from the imports
807      * @return Returns <code>true</code> if the import was found and removed
808      */

809     public boolean removeStaticImport(String JavaDoc qualifiedName) {
810         String JavaDoc containerName= Signature.getQualifier(qualifiedName);
811         
812         int nPackages= fPackageEntries.size();
813         for (int i= 0; i < nPackages; i++) {
814             PackageEntry entry= (PackageEntry) fPackageEntries.get(i);
815             if (entry.compareTo(containerName, true) == 0) {
816                 if (entry.remove(qualifiedName, true)) {
817                     fHasChanges= true;
818                     return true;
819                 }
820             }
821         }
822         return false;
823     }
824     
825     
826     /**
827      * Removes an import from the structure.
828      * @param binding The type to remove from the imports
829      * @return Returns <code>true</code> if the import was found and removed
830      */

831     public boolean removeImport(ITypeBinding binding) {
832         binding= Bindings.normalizeTypeBinding(binding);
833         if (binding == null) {
834             return false;
835         }
836         String JavaDoc qualifiedName= Bindings.getRawQualifiedName(binding);
837         if (qualifiedName.length() > 0) {
838             return removeImport(qualifiedName);
839         }
840         return false;
841     }
842
843     /**
844      * Looks if there already is single import for the given type name.
845      * @param simpleName The simple name to find
846      * @return Returns the qualified import name or <code>null</code>.
847      */

848     public String JavaDoc findImport(String JavaDoc simpleName) {
849         int nPackages= fPackageEntries.size();
850         for (int i= 0; i < nPackages; i++) {
851             PackageEntry entry= (PackageEntry) fPackageEntries.get(i);
852             if (!entry.isStatic()) {
853                 ImportDeclEntry found= entry.find(simpleName);
854                 if (found != null) {
855                     return found.getElementName();
856                 }
857             }
858         }
859         return null;
860     }
861         
862     public String JavaDoc findStaticImport(String JavaDoc typeContainerName, String JavaDoc typeSimpleName) {
863         int nPackages= fPackageEntries.size();
864         for (int i= 0; i < nPackages; i++) {
865             PackageEntry entry= (PackageEntry) fPackageEntries.get(i);
866             if (entry.isStatic()) {
867                 if (typeContainerName == null || entry.getName().equals(typeContainerName)) {
868                     ImportDeclEntry found= entry.find(typeSimpleName);
869                     if (found != null) {
870                         return found.getElementName();
871                     }
872                 }
873             }
874         }
875         return null;
876     }
877     
878     /**
879      * Creates all new elements in the import structure.
880      * @param save Save the CU after the import have been changed
881      * @param monitor The progress monitor to use
882      * @throws CoreException Thrown when the access to the CU failed
883      */

884     public void create(boolean save, IProgressMonitor monitor) throws CoreException {
885         if (monitor == null) {
886             monitor= new NullProgressMonitor();
887         }
888         monitor.beginTask(CodeGenerationMessages.ImportsStructure_operation_description, 4);
889         
890         IDocument document= null;
891         DocumentRewriteSession session= null;
892         try {
893             document= aquireDocument(new SubProgressMonitor(monitor, 1));
894             if (document instanceof IDocumentExtension4) {
895                  session= ((IDocumentExtension4)document).startRewriteSession(
896                     DocumentRewriteSessionType.UNRESTRICTED);
897             }
898             MultiTextEdit edit= getResultingEdits(document, new SubProgressMonitor(monitor, 1));
899             if (edit.hasChildren()) {
900                 if (save) {
901                     commitDocument(document, edit, new SubProgressMonitor(monitor, 1));
902                 } else {
903                     edit.apply(document);
904                 }
905             }
906         } catch (BadLocationException e) {
907             throw new CoreException(JavaUIStatus.createError(IStatus.ERROR, e));
908         } finally {
909             try {
910                 if (session != null) {
911                     ((IDocumentExtension4)document).stopRewriteSession(session);
912                 }
913             } finally {
914                 releaseDocument(document, new SubProgressMonitor(monitor, 1));
915             }
916             monitor.done();
917         }
918     }
919     
920     private IDocument aquireDocument(IProgressMonitor monitor) throws CoreException {
921         if (JavaModelUtil.isPrimary(fCompilationUnit)) {
922             IFile file= (IFile) fCompilationUnit.getResource();
923             if (file.exists()) {
924                 ITextFileBufferManager bufferManager= FileBuffers.getTextFileBufferManager();
925                 IPath path= fCompilationUnit.getPath();
926                 bufferManager.connect(path, monitor);
927                 return bufferManager.getTextFileBuffer(path).getDocument();
928             }
929         }
930         monitor.done();
931         return new Document(fCompilationUnit.getSource());
932     }
933     
934     private void releaseDocument(IDocument document, IProgressMonitor monitor) throws CoreException {
935         if (JavaModelUtil.isPrimary(fCompilationUnit)) {
936             IFile file= (IFile) fCompilationUnit.getResource();
937             if (file.exists()) {
938                 ITextFileBufferManager bufferManager= FileBuffers.getTextFileBufferManager();
939                 bufferManager.disconnect(file.getFullPath(), monitor);
940                 return;
941             }
942         }
943         fCompilationUnit.getBuffer().setContents(document.get());
944         monitor.done();
945     }
946     
947     private void commitDocument(IDocument document, MultiTextEdit edit, IProgressMonitor monitor) throws CoreException, MalformedTreeException, BadLocationException {
948         if (JavaModelUtil.isPrimary(fCompilationUnit)) {
949             IFile file= (IFile) fCompilationUnit.getResource();
950             if (file.exists()) {
951                 IStatus status= Resources.makeCommittable(file, null);
952                 if (!status.isOK()) {
953                     throw new ValidateEditException(status);
954                 }
955                 edit.apply(document); // apply after file is committable
956

957                 ITextFileBufferManager bufferManager= FileBuffers.getTextFileBufferManager();
958                 bufferManager.getTextFileBuffer(file.getFullPath()).commit(monitor, true);
959                 return;
960             }
961         }
962         // no commit possible, make sure changes are in
963
edit.apply(document);
964     }
965         
966     private IRegion evaluateReplaceRange(IDocument document) throws JavaModelException, BadLocationException {
967         JavaModelUtil.reconcile(fCompilationUnit);
968
969         IImportContainer container= fCompilationUnit.getImportContainer();
970         if (container.exists()) {
971             ISourceRange importSourceRange= container.getSourceRange();
972             int startPos= importSourceRange.getOffset();
973             int endPos= startPos + importSourceRange.getLength();
974             if (!Strings.isLineDelimiterChar(document.getChar(endPos - 1))) {
975                 // if not already after a new line, go to beginning of next line
976
// (if last char in new line -> import ends with a comment, see 10557)
977
int nextLine= document.getLineOfOffset(endPos) + 1;
978                 if (nextLine < document.getNumberOfLines()) {
979                     int stopPos= document.getLineInformation(nextLine).getOffset();
980                     // read to beginning of next character or beginning of next line
981
while (endPos < stopPos && Character.isWhitespace(document.getChar(endPos))) {
982                         endPos++;
983                     }
984                 }
985             }
986             return new Region(startPos, endPos - startPos);
987         } else {
988             int start= getPackageStatementEndPos(document);
989             return new Region(start, 0);
990         }
991     }
992     
993     public MultiTextEdit getResultingEdits(IDocument document, IProgressMonitor monitor) throws JavaModelException, BadLocationException {
994         if (monitor == null) {
995             monitor= new NullProgressMonitor();
996         }
997         try {
998             fImportsCreated= new ArrayList JavaDoc();
999             fStaticImportsCreated= new ArrayList JavaDoc();
1000        
1001            int importsStart= fReplaceRange.getOffset();
1002            int importsLen= fReplaceRange.getLength();
1003                    
1004            String JavaDoc lineDelim= TextUtilities.getDefaultLineDelimiter(document);
1005            boolean useSpaceBetween= useSpaceBetweenGroups();
1006                        
1007            int currPos= importsStart;
1008            MultiTextEdit resEdit= new MultiTextEdit();
1009            
1010            if (importsLen == 0) {
1011                // new import container
1012
resEdit.addChild(new InsertEdit(currPos, lineDelim)); // first entry, might be removed later
1013
}
1014            
1015            PackageEntry lastPackage= null;
1016            
1017            Set JavaDoc onDemandConflicts= null;
1018            if (fFindAmbiguousImports) {
1019                onDemandConflicts= evaluateStarImportConflicts(monitor);
1020            }
1021            
1022            ArrayList JavaDoc stringsToInsert= new ArrayList JavaDoc();
1023            
1024            int nPackageEntries= fPackageEntries.size();
1025            for (int i= 0; i < nPackageEntries; i++) {
1026                PackageEntry pack= (PackageEntry) fPackageEntries.get(i);
1027                int nImports= pack.getNumberOfImports();
1028    
1029                if (fFilterImplicitImports && !pack.isStatic() && isImplicitImport(pack.getName(), fCompilationUnit)) {
1030                    pack.removeAllNew(onDemandConflicts);
1031                    nImports= pack.getNumberOfImports();
1032                }
1033                if (nImports == 0) {
1034                    continue;
1035                }
1036                
1037                if (useSpaceBetween) {
1038                    // add a space between two different groups by looking at the two adjacent imports
1039
if (lastPackage != null && !pack.isComment() && !pack.isSameGroup(lastPackage)) {
1040                        ImportDeclEntry last= lastPackage.getImportAt(lastPackage.getNumberOfImports() - 1);
1041                        ImportDeclEntry first= pack.getImportAt(0);
1042                        if (!lastPackage.isComment() && (last.isNew() || first.isNew())) {
1043                            stringsToInsert.add(lineDelim);
1044                        }
1045                    }
1046                }
1047                lastPackage= pack;
1048                
1049                boolean isStatic= pack.isStatic();
1050                
1051                boolean doStarImport= pack.hasStarImport(fImportOnDemandThreshold, onDemandConflicts);
1052                if (doStarImport && (pack.find("*") == null)) { //$NON-NLS-1$
1053
String JavaDoc starImportString= pack.getName() + ".*"; //$NON-NLS-1$
1054
String JavaDoc str= getNewImportString(starImportString, isStatic, lineDelim);
1055                    stringsToInsert.add(str);
1056                }
1057                
1058                for (int k= 0; k < nImports; k++) {
1059                    ImportDeclEntry currDecl= pack.getImportAt(k);
1060                    IRegion region= currDecl.getSourceRange();
1061                    
1062                    if (region == null) { // new entry
1063
if (!doStarImport || currDecl.isOnDemand() || (onDemandConflicts != null && onDemandConflicts.contains(currDecl.getSimpleName()))) {
1064                            String JavaDoc str= getNewImportString(currDecl.getElementName(), isStatic, lineDelim);
1065                            stringsToInsert.add(str);
1066                        }
1067                    } else {
1068                        if (!doStarImport || currDecl.isOnDemand() || onDemandConflicts == null || onDemandConflicts.contains(currDecl.getSimpleName())) {
1069                            int offset= region.getOffset();
1070                            removeAndInsertNew(document, currPos, offset, stringsToInsert, resEdit);
1071                            stringsToInsert.clear();
1072                            currPos= offset + region.getLength();
1073                        }
1074                    }
1075                }
1076            }
1077            
1078            int end= importsStart + importsLen;
1079            removeAndInsertNew(document, currPos, end, stringsToInsert, resEdit);
1080            
1081            if (importsLen == 0) {
1082                if (!fImportsCreated.isEmpty() || !fStaticImportsCreated.isEmpty()) { // new import container
1083
if (fCompilationUnit.getPackageDeclarations().length == 0) { // no package statement
1084
resEdit.removeChild(0);
1085                    }
1086                    // check if a space between import and first type is needed
1087
IType[] types= fCompilationUnit.getTypes();
1088                    if (types.length > 0) {
1089                        if (types[0].getSourceRange().getOffset() == importsStart) {
1090                            resEdit.addChild(new InsertEdit(currPos, lineDelim));
1091                        }
1092                    }
1093                } else {
1094                    return new MultiTextEdit(); // no changes
1095
}
1096            }
1097            return resEdit;
1098        } finally {
1099            monitor.done();
1100        }
1101    }
1102    
1103    private void removeAndInsertNew(IDocument doc, int contentOffset, int contentEnd, ArrayList JavaDoc stringsToInsert, MultiTextEdit resEdit) throws BadLocationException {
1104        int pos= contentOffset;
1105        for (int i= 0; i < stringsToInsert.size(); i++) {
1106            String JavaDoc curr= (String JavaDoc) stringsToInsert.get(i);
1107            int idx= findInDocument(doc, curr, pos, contentEnd);
1108            if (idx != -1) {
1109                if (idx != pos) {
1110                    resEdit.addChild(new DeleteEdit(pos, idx - pos));
1111                }
1112                pos= idx + curr.length();
1113            } else {
1114                resEdit.addChild(new InsertEdit(pos, curr));
1115            }
1116        }
1117        if (pos < contentEnd) {
1118            resEdit.addChild(new DeleteEdit(pos, contentEnd - pos));
1119        }
1120    }
1121
1122    private int findInDocument(IDocument doc, String JavaDoc str, int start, int end) throws BadLocationException {
1123        int pos= start;
1124        int len= str.length();
1125        if (pos + len > end || str.length() == 0) {
1126            return -1;
1127        }
1128        char first= str.charAt(0);
1129        int step= str.indexOf(first, 1);
1130        if (step == -1) {
1131            step= len;
1132        }
1133        while (pos + len <= end) {
1134            if (doc.getChar(pos) == first) {
1135                int k= 1;
1136                while (k < len && doc.getChar(pos + k) == str.charAt(k)) {
1137                    k++;
1138                }
1139                if (k == len) {
1140                    return pos; // found
1141
}
1142                if (k < step) {
1143                    pos+= k;
1144                } else {
1145                    pos+= step;
1146                }
1147            } else {
1148                pos++;
1149            }
1150        }
1151        return -1;
1152    }
1153    
1154    
1155    /**
1156     * @return Probes if the formatter allows spaces between imports
1157     */

1158    private boolean useSpaceBetweenGroups() {
1159        try {
1160            String JavaDoc sample= "import a.A;\n\n import b.B;\nclass C {}"; //$NON-NLS-1$
1161
TextEdit res= ToolFactory.createCodeFormatter(fCompilationUnit.getJavaProject().getOptions(true)).format(CodeFormatter.K_COMPILATION_UNIT, sample, 0, sample.length(), 0, String.valueOf('\n'));
1162            Document doc= new Document(sample);
1163            res.apply(doc);
1164            int idx1= doc.search(0, "import", true, true, false); //$NON-NLS-1$
1165
int line1= doc.getLineOfOffset(idx1);
1166            int idx2= doc.search(idx1 + 1, "import", true, true, false); //$NON-NLS-1$
1167
int line2= doc.getLineOfOffset(idx2);
1168            return line2 - line1 > 1;
1169        } catch (BadLocationException e) {
1170            // should not happen
1171
}
1172        return true;
1173    }
1174
1175    private Set JavaDoc evaluateStarImportConflicts(IProgressMonitor monitor) throws JavaModelException {
1176        //long start= System.currentTimeMillis();
1177

1178        final HashSet JavaDoc/*String*/ onDemandConflicts= new HashSet JavaDoc();
1179        
1180        IJavaSearchScope scope= SearchEngine.createJavaSearchScope(new IJavaElement[] { fCompilationUnit.getJavaProject() });
1181
1182        ArrayList JavaDoc/*<char[][]>*/ starImportPackages= new ArrayList JavaDoc();
1183        ArrayList JavaDoc/*<char[][]>*/ simpleTypeNames= new ArrayList JavaDoc();
1184        int nPackageEntries= fPackageEntries.size();
1185        for (int i= 0; i < nPackageEntries; i++) {
1186            PackageEntry pack= (PackageEntry) fPackageEntries.get(i);
1187            if (!pack.isStatic() && pack.hasStarImport(fImportOnDemandThreshold, null)) {
1188                starImportPackages.add(pack.getName().toCharArray());
1189                for (int k= 0; k < pack.getNumberOfImports(); k++) {
1190                    ImportDeclEntry curr= pack.getImportAt(k);
1191                    if (!curr.isOnDemand() && !curr.isComment()) {
1192                        simpleTypeNames.add(curr.getSimpleName().toCharArray());
1193                    }
1194                }
1195            }
1196        }
1197        if (starImportPackages.isEmpty()) {
1198            return null;
1199        }
1200        
1201        starImportPackages.add(fCompilationUnit.getParent().getElementName().toCharArray());
1202        starImportPackages.add(JAVA_LANG.toCharArray());
1203        
1204        char[][] allPackages= (char[][]) starImportPackages.toArray(new char[starImportPackages.size()][]);
1205        char[][] allTypes= (char[][]) simpleTypeNames.toArray(new char[simpleTypeNames.size()][]);
1206        
1207        TypeNameRequestor requestor= new TypeNameRequestor() {
1208            HashMap JavaDoc foundTypes= new HashMap JavaDoc();
1209            
1210            private String JavaDoc getTypeContainerName(char[] packageName, char[][] enclosingTypeNames) {
1211                StringBuffer JavaDoc buf= new StringBuffer JavaDoc();
1212                buf.append(packageName);
1213                for (int i= 0; i < enclosingTypeNames.length; i++) {
1214                    if (buf.length() > 0)
1215                        buf.append('.');
1216                    buf.append(enclosingTypeNames[i]);
1217                }
1218                return buf.toString();
1219            }
1220            
1221            public void acceptType(int flags, char[] packageName, char[] simpleTypeName, char[][] enclosingTypeNames, String JavaDoc path) {
1222                String JavaDoc name= new String JavaDoc(simpleTypeName);
1223                String JavaDoc containerName= getTypeContainerName(packageName, enclosingTypeNames);
1224                
1225                String JavaDoc oldContainer= (String JavaDoc) foundTypes.put(name, containerName);
1226                if (oldContainer != null && !oldContainer.equals(containerName)) {
1227                    onDemandConflicts.add(name);
1228                }
1229            }
1230        };
1231        new SearchEngine().searchAllTypeNames(allPackages, allTypes, scope, requestor, IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, monitor);
1232        return onDemandConflicts;
1233    }
1234        
1235    private String JavaDoc getNewImportString(String JavaDoc importName, boolean isStatic, String JavaDoc lineDelim) {
1236        StringBuffer JavaDoc buf= new StringBuffer JavaDoc();
1237        buf.append("import "); //$NON-NLS-1$
1238
if (isStatic) {
1239            buf.append("static "); //$NON-NLS-1$
1240
}
1241        buf.append(importName);
1242        buf.append(';');
1243        buf.append(lineDelim);
1244        // str= StubUtility.codeFormat(str, 0, lineDelim);
1245

1246        if (isStatic) {
1247            fStaticImportsCreated.add(importName);
1248        } else {
1249            fImportsCreated.add(importName);
1250        }
1251        return buf.toString();
1252    }
1253    
1254    private int getPackageStatementEndPos(IDocument document) throws JavaModelException, BadLocationException {
1255        IPackageDeclaration[] packDecls= fCompilationUnit.getPackageDeclarations();
1256        if (packDecls != null && packDecls.length > 0) {
1257            ISourceRange range= packDecls[0].getSourceRange();
1258            int line= document.getLineOfOffset(range.getOffset() + range.getLength());
1259            IRegion region= document.getLineInformation(line + 1);
1260            if (region != null) {
1261                IType[] types= fCompilationUnit.getTypes();
1262                if (types.length > 0) {
1263                    return Math.min(types[0].getSourceRange().getOffset(), region.getOffset());
1264                }
1265                return region.getOffset();
1266            }
1267        }
1268        return 0;
1269    }
1270    
1271    public String JavaDoc toString() {
1272        int nPackages= fPackageEntries.size();
1273        StringBuffer JavaDoc buf= new StringBuffer JavaDoc("\n-----------------------\n"); //$NON-NLS-1$
1274
for (int i= 0; i < nPackages; i++) {
1275            PackageEntry entry= (PackageEntry) fPackageEntries.get(i);
1276            if (entry.isStatic()) {
1277                buf.append("static "); //$NON-NLS-1$
1278
}
1279            buf.append(entry.toString());
1280        }
1281        return buf.toString();
1282    }
1283    
1284    private static final class ImportDeclEntry {
1285        
1286        private String JavaDoc fElementName;
1287        private IRegion fSourceRange;
1288        private final boolean fIsStatic;
1289        
1290        public ImportDeclEntry(String JavaDoc elementName, boolean isStatic, IRegion sourceRange) {
1291            fElementName= elementName;
1292            fSourceRange= sourceRange;
1293            fIsStatic= isStatic;
1294        }
1295                
1296        public String JavaDoc getElementName() {
1297            return fElementName;
1298        }
1299        
1300        public int compareTo(String JavaDoc fullName, boolean isStatic) {
1301            int cmp= fElementName.compareTo(fullName);
1302            if (cmp == 0) {
1303                if (fIsStatic == isStatic) {
1304                    return 0;
1305                }
1306                return fIsStatic ? -1 : 1;
1307            }
1308            return cmp;
1309        }
1310        
1311        public String JavaDoc getSimpleName() {
1312            return Signature.getSimpleName(fElementName);
1313        }
1314        
1315        public boolean isOnDemand() {
1316            return fElementName != null && fElementName.endsWith(".*"); //$NON-NLS-1$
1317
}
1318        
1319        public boolean isStatic() {
1320            return fIsStatic;
1321        }
1322            
1323        public boolean isNew() {
1324            return fSourceRange == null;
1325        }
1326        
1327        public boolean isComment() {
1328            return fElementName == null;
1329        }
1330        
1331        public IRegion getSourceRange() {
1332            return fSourceRange;
1333        }
1334                
1335    }
1336    
1337    /*
1338     * Internal element for the import structure: A container for imports
1339     * of all types from the same package
1340     */

1341    private final static class PackageEntry {
1342        
1343        public static PackageEntry createOnPlaceholderEntry(String JavaDoc preferenceOrder) {
1344            if (preferenceOrder.length() > 0 && preferenceOrder.charAt(0) == '#') {
1345                String JavaDoc curr= preferenceOrder.substring(1);
1346                return new PackageEntry(curr, curr, true);
1347            }
1348            return new PackageEntry(preferenceOrder, preferenceOrder, false);
1349        }
1350        
1351        private String JavaDoc fName;
1352        private ArrayList JavaDoc fImportEntries;
1353        private String JavaDoc fGroup;
1354        private boolean fIsStatic;
1355    
1356        /**
1357         * Comment package entry
1358         */

1359        public PackageEntry() {
1360            this("!", null, false); //$NON-NLS-1$
1361
}
1362        
1363        /**
1364         * @param name Name of the package entry. e.g. org.eclipse.jdt.ui, containing imports like
1365         * org.eclipse.jdt.ui.JavaUI.
1366         * @param group The index of the preference order entry assigned
1367         * different group id's will result in spacers between the entries
1368         */

1369        public PackageEntry(String JavaDoc name, String JavaDoc group, boolean isStatic) {
1370            fName= name;
1371            fImportEntries= new ArrayList JavaDoc(5);
1372            fGroup= group;
1373            fIsStatic= isStatic;
1374        }
1375        
1376        public boolean isStatic() {
1377            return fIsStatic;
1378        }
1379        
1380        public int compareTo(String JavaDoc name, boolean isStatic) {
1381            int cmp= fName.compareTo(name);
1382            if (cmp == 0) {
1383                if (fIsStatic == isStatic) {
1384                    return 0;
1385                }
1386                return fIsStatic ? -1 : 1;
1387            }
1388            return cmp;
1389        }
1390                        
1391        public void sortIn(ImportDeclEntry imp) {
1392            String JavaDoc fullImportName= imp.getElementName();
1393            int insertPosition= -1;
1394            int nInports= fImportEntries.size();
1395            for (int i= 0; i < nInports; i++) {
1396                ImportDeclEntry curr= getImportAt(i);
1397                if (!curr.isComment()) {
1398                    int cmp= curr.compareTo(fullImportName, imp.isStatic());
1399                    if (cmp == 0) {
1400                        return; // exists already
1401
} else if (cmp > 0 && insertPosition == -1) {
1402                        insertPosition= i;
1403                    }
1404                }
1405            }
1406            if (insertPosition == -1) {
1407                fImportEntries.add(imp);
1408            } else {
1409                fImportEntries.add(insertPosition, imp);
1410            }
1411        }
1412        
1413        
1414        public void add(ImportDeclEntry imp) {
1415            fImportEntries.add(imp);
1416        }
1417        
1418        public ImportDeclEntry find(String JavaDoc simpleName) {
1419            int nInports= fImportEntries.size();
1420            for (int i= 0; i < nInports; i++) {
1421                ImportDeclEntry curr= getImportAt(i);
1422                if (!curr.isComment()) {
1423                    String JavaDoc name= curr.getElementName();
1424                    if (name.endsWith(simpleName)) {
1425                        int dotPos= name.length() - simpleName.length() - 1;
1426                        if ((dotPos == -1) || (dotPos > 0 && name.charAt(dotPos) == '.')) {
1427                            return curr;
1428                        }
1429                    }
1430                }
1431            }
1432            return null;
1433        }
1434        
1435        public boolean remove(String JavaDoc fullName, boolean isStaticImport) {
1436            int nInports= fImportEntries.size();
1437            for (int i= 0; i < nInports; i++) {
1438                ImportDeclEntry curr= getImportAt(i);
1439                if (!curr.isComment() && curr.compareTo(fullName, isStaticImport) == 0) {
1440                    fImportEntries.remove(i);
1441                    return true;
1442                }
1443            }
1444            return false;
1445        }
1446        
1447        public void removeAllNew(Set JavaDoc onDemandConflicts) {
1448            int nInports= fImportEntries.size();
1449            for (int i= nInports - 1; i >= 0; i--) {
1450                ImportDeclEntry curr= getImportAt(i);
1451                if (curr.isNew() /*&& (onDemandConflicts == null || onDemandConflicts.contains(curr.getSimpleName()))*/) {
1452                    fImportEntries.remove(i);
1453                }
1454            }
1455        }
1456        
1457        public ImportDeclEntry getImportAt(int index) {
1458            return (ImportDeclEntry) fImportEntries.get(index);
1459        }
1460        
1461        public boolean hasStarImport(int threshold, Set JavaDoc explicitImports) {
1462            if (isComment() || isDefaultPackage()) { // can not star import default package
1463
return false;
1464            }
1465            int nImports= getNumberOfImports();
1466            int count= 0;
1467            boolean containsNew= false;
1468            for (int i= 0; i < nImports; i++) {
1469                ImportDeclEntry curr= getImportAt(i);
1470                if (curr.isOnDemand()) {
1471                    return true;
1472                }
1473                if (!curr.isComment()) {
1474                    count++;
1475                    boolean isExplicit= !curr.isStatic() && (explicitImports != null) && explicitImports.contains(curr.getSimpleName());
1476                    containsNew |= curr.isNew() && !isExplicit;
1477                }
1478            }
1479            return (count >= threshold) && containsNew;
1480        }
1481        
1482        public int getNumberOfImports() {
1483            return fImportEntries.size();
1484        }
1485            
1486        public String JavaDoc getName() {
1487            return fName;
1488        }
1489        
1490        public String JavaDoc getGroupID() {
1491            return fGroup;
1492        }
1493        
1494        public void setGroupID(String JavaDoc groupID) {
1495            fGroup= groupID;
1496        }
1497        
1498        public boolean isSameGroup(PackageEntry other) {
1499            if (fGroup == null) {
1500                return other.getGroupID() == null;
1501            } else {
1502                return fGroup.equals(other.getGroupID()) && (fIsStatic == other.isStatic());
1503            }
1504        }
1505                
1506        public ImportDeclEntry getLast() {
1507            int nImports= getNumberOfImports();
1508            if (nImports > 0) {
1509                return getImportAt(nImports - 1);
1510            }
1511            return null;
1512        }
1513        
1514        public boolean isComment() {
1515            return "!".equals(fName); //$NON-NLS-1$
1516
}
1517        
1518        public boolean isDefaultPackage() {
1519            return fName.length() == 0;
1520        }
1521        
1522        public String JavaDoc toString() {
1523            StringBuffer JavaDoc buf= new StringBuffer JavaDoc();
1524            if (isComment()) {
1525                buf.append("comment\n"); //$NON-NLS-1$
1526
} else {
1527                buf.append(fName); buf.append(", groupId: "); buf.append(fGroup); buf.append("\n"); //$NON-NLS-1$ //$NON-NLS-2$
1528
int nImports= getNumberOfImports();
1529                for (int i= 0; i < nImports; i++) {
1530                    ImportDeclEntry curr= getImportAt(i);
1531                    buf.append(" "); //$NON-NLS-1$
1532
if (curr.isStatic()) {
1533                        buf.append("static "); //$NON-NLS-1$
1534
}
1535                    buf.append(curr.getSimpleName());
1536                    if (curr.isNew()) {
1537                        buf.append(" (new)"); //$NON-NLS-1$
1538
}
1539                    buf.append("\n"); //$NON-NLS-1$
1540
}
1541            }
1542            return buf.toString();
1543        }
1544    }
1545    
1546    public String JavaDoc[] getCreatedImports() {
1547        if (fImportsCreated == null) {
1548            return new String JavaDoc[0];
1549        }
1550        return (String JavaDoc[]) fImportsCreated.toArray(new String JavaDoc[fImportsCreated.size()]);
1551    }
1552    
1553    public String JavaDoc[] getCreatedStaticImports() {
1554        if (fStaticImportsCreated == null) {
1555            return new String JavaDoc[0];
1556        }
1557        return (String JavaDoc[]) fStaticImportsCreated.toArray(new String JavaDoc[fStaticImportsCreated.size()]);
1558    }
1559
1560    /**
1561     * Returns <code>true</code> if imports have been added or removed.
1562     * @return boolean
1563     */

1564    public boolean hasChanges() {
1565        return fHasChanges;
1566    }
1567
1568
1569
1570}
1571
Popular Tags