@@ -360,6 +360,32 @@ private IType[] resolveMissingTypes(IProgressMonitor monitor) throws JavaModelEx
360360 }
361361 }
362362
363+ /**
364+ * Determines if organize imports is unsafe due to syntax errors or other conditions.
365+ */
366+ private static boolean isUnclean (ModuleNodeInfo info , GroovyCompilationUnit unit ) {
367+ try {
368+ if (info .module .encounteredUnrecoverableError () || !unit .isConsistent ()) {
369+ return true ;
370+ }
371+ CategorizedProblem [] problems = info .result .getProblems ();
372+ if (problems != null && problems .length > 0 ) {
373+ for (CategorizedProblem problem : problems ) {
374+ if (problem .isError () && problem .getCategoryID () == CategorizedProblem .CAT_INTERNAL ) {
375+ String message = problem .getMessage ();
376+ if (message .contains ("unexpected token" )) {
377+ trace ("Stopping due to error in compilation unit: %s" , message );
378+ return true ;
379+ }
380+ }
381+ }
382+ }
383+ } catch (Exception e ) {
384+ return true ;
385+ }
386+ return false ;
387+ }
388+
363389 /**
364390 * GRECLIPSE-1390
365391 * Reorganizing imports (ie- sorting and grouping them) will remove annotations on import statements
@@ -376,16 +402,16 @@ private static boolean isSafeToReorganize(Iterable<ImportNode> allImports) {
376402 return true ;
377403 }
378404
379- private static boolean isAliased (ImportNode imp ) {
380- String alias = imp .getAlias ();
405+ private static boolean isAliased (ImportNode node ) {
406+ String alias = node .getAlias ();
381407 if (alias == null ) {
382408 return false ;
383409 }
384- String fieldName = imp .getFieldName ();
410+ String fieldName = node .getFieldName ();
385411 if (fieldName != null ) {
386412 return !fieldName .equals (alias );
387413 }
388- String className = imp .getClassName ();
414+ String className = node .getClassName ();
389415 if (className != null ) {
390416 // it is possible to import from the default package
391417 boolean aliasIsSameAsClassName = className .endsWith (alias ) &&
@@ -395,30 +421,14 @@ private static boolean isAliased(ImportNode imp) {
395421 return false ;
396422 }
397423
398- /**
399- * Determines if organize imports is unsafe due to syntax errors or other conditions.
400- */
401- private static boolean isUnclean (ModuleNodeInfo info , GroovyCompilationUnit unit ) {
402- try {
403- if (info .module .encounteredUnrecoverableError () || !unit .isConsistent ()) {
404- return true ;
405- }
406- CategorizedProblem [] problems = info .result .getProblems ();
407- if (problems != null && problems .length > 0 ) {
408- for (CategorizedProblem problem : problems ) {
409- if (problem .isError () && problem .getCategoryID () == CategorizedProblem .CAT_INTERNAL ) {
410- String message = problem .getMessage ();
411- if (message .contains ("unexpected token" )) {
412- trace ("Stopping due to error in compilation unit: %s" , message );
413- return true ;
414- }
415- }
416- }
417- }
418- } catch (Exception e ) {
419- return true ;
424+ private static String getTypeName (ClassNode node ) {
425+ ClassNode type = GroovyUtils .getBaseType (node );
426+ // unresolved name may have dots and/or dollars (e.g. 'a.b.C$D' or 'C$D' or even 'C.D')
427+ if (!type .getName ().matches (".*\\ b" + type .getUnresolvedName ().replace ('$' , '.' ))) {
428+ // synch up name and unresolved name (e.g. 'java.util.Map$Entry as Foo$Entry')
429+ return type .getName () + " as " + type .getUnresolvedName ().replace ('.' , '$' );
420430 }
421- return false ;
431+ return type . getName () ;
422432 }
423433
424434 private static void trace (String message , Object ... arguments ) {
@@ -691,10 +701,9 @@ private void handleTypeReference(ClassNode node, boolean isAnnotation) {
691701 name = name .replaceAll (Pattern .quote (name .substring (i )) + "(?= |$)" , "" );
692702 }
693703 doNotRemoveImport (name .replace ('$' , '.' ));
694- return ;
695- }
696-
697- if (!node .isResolved () && !current .getModule ().getClasses ().contains (node .redirect ())) {
704+ } else if (current .getModule ().getClasses ().contains (node )) {
705+ // keep in importsSlatedForRemoval and leave out of missingTypes
706+ } else if (!node .isResolved ()) {
698707 String [] parts = name .split ("\\ ." );
699708 if (Character .isUpperCase (name .charAt (0 ))) {
700709 name = parts [0 ]; // 'Map.Entry' -> 'Map'
@@ -714,15 +723,17 @@ private void handleTypeReference(ClassNode node, boolean isAnnotation) {
714723 while (i < chars .length && Character .isJavaIdentifierPart (chars [i ])) {
715724 i += 1 ;
716725 }
726+ // for a 'Map.Entry' reference find '.Map' in 'java.util.Map' or '.Map$' in 'java.util.Map$Entry'
717727 m = Pattern .compile ("(?:\\ A|\\ $|\\ .)" + String .valueOf (chars , 0 , i ) + "(?=\\ $|$)" ).matcher (name );
718728 if (m .find ()) {
719729 // 'java.util.Map$Entry' -> 'java.util.Map'
720- String partialName = name .substring (0 , m .end ()).replace ('$' , '.' );
721- doNotRemoveImport (partialName );
730+ String partialName = name .substring (0 , m .end ());
731+ if (!isInnerOfSuper (partialName ))
732+ doNotRemoveImport (partialName .replace ('$' , '.' ));
722733 }
723734 } else {
724- // We don't know exactly what the text is. We just know how
725- // it resolves. This can be a problem if an inner class. We
735+ // We do not know exactly what the text is. We just know how
736+ // it resolves. This can be a problem for an inner class. We
726737 // don't really know what is in the text nor what the import
727738 // is, so just ensure that none are slated for removal.
728739 String partialName = name .replace ('$' , '.' );
@@ -740,14 +751,31 @@ private void handleTypeReference(ClassNode node, boolean isAnnotation) {
740751 }
741752 }
742753
743- private String getTypeName (ClassNode node ) {
744- ClassNode type = GroovyUtils .getBaseType (node );
745- // unresolved name may have dots and/or dollars (e.g. 'a.b.C$D' or 'C$D' or even 'C.D')
746- if (!type .getName ().matches (".*\\ b" + type .getUnresolvedName ().replace ('$' , '.' ))) {
747- // synch up name and unresolved name (e.g. 'java.util.Map$Entry as Foo$Entry')
748- return type .getName () + " as " + type .getUnresolvedName ().replace ('.' , '$' );
754+ private boolean isInnerOfSuper (String name ) {
755+ if (name .lastIndexOf ('$' ) > 0 ) {
756+ for (ClassNode node = current .getSuperClass (); node != null && !node .equals (ClassHelper .OBJECT_TYPE ); node = node .getSuperClass ()) {
757+ for (Iterator <? extends ClassNode > it = node .redirect ().getInnerClasses (); it .hasNext ();) {
758+ if (it .next ().getName ().equals (name )) {
759+ return true ;
760+ }
761+ }
762+ }
763+
764+ java .util .Queue <ClassNode > todo = new java .util .ArrayDeque <>();
765+ java .util .Collections .addAll (todo , current .getInterfaces ());
766+ Set <ClassNode > done = new LinkedHashSet <>();
767+ ClassNode node ;
768+
769+ while ((node = todo .poll ()) != null ) { if (!done .add (node )) continue ;
770+ for (Iterator <? extends ClassNode > it = node .redirect ().getInnerClasses (); it .hasNext ();) {
771+ if (it .next ().getName ().equals (name )) {
772+ return true ;
773+ }
774+ }
775+ java .util .Collections .addAll (todo , node .getInterfaces ());
776+ }
749777 }
750- return type . getName () ;
778+ return false ;
751779 }
752780
753781 private boolean checkRetainImport (String name ) {
0 commit comments