3939import java .util .ArrayList ;
4040import java .util .Arrays ;
4141import java .util .Comparator ;
42- import java .util .HashMap ;
4342import java .util .HashSet ;
4443import java .util .List ;
4544import java .util .Map ;
4645import java .util .Set ;
46+ import java .util .TreeMap ;
4747import java .util .concurrent .CopyOnWriteArrayList ;
4848import java .util .concurrent .CountDownLatch ;
4949import java .util .concurrent .ExecutionException ;
@@ -124,7 +124,15 @@ public class IndexDatabase {
124124 private final Object INSTANCE_LOCK = new Object ();
125125
126126 /** Key is canonical path; Value is the first accepted, absolute path. */
127- private final Map <String , String > acceptedNonlocalSymlinks = new HashMap <>();
127+ private final Map <String , String > acceptedNonlocalSymlinks = new TreeMap <>((o1 , o2 ) -> {
128+ // DESC by length.
129+ int cmp = Integer .compare (o2 .length (), o1 .length ());
130+ if (cmp != 0 ) {
131+ return cmp ;
132+ }
133+ // ASC by String value.
134+ return o1 .compareTo (o2 );
135+ });
128136
129137 private Project project ;
130138 private FSDirectory indexDirectory ;
@@ -957,44 +965,84 @@ private boolean acceptSymlink(Path absolute, File canonical, AcceptSymlinkRet re
957965 new Object [] {canonical1 , absolute1 });
958966 }
959967 return true ;
960- } else {
961- /*
962- * Do not accept symlinks to local directories, because the
963- * canonical target will be indexed on its own -- but
964- * relativize() a path to be returned in ret so that
965- * a symlink can be replicated in xref/.
966- */
967- ret .localRelPath = absolute .getParent ().relativize (
968- canonical .toPath ()).toString ();
969- return false ;
970968 }
969+
970+ /*
971+ * Do not accept symlinks to local directories, because the
972+ * canonical target will be indexed on its own -- but relativize()
973+ * a path to be returned in ret so that a symlink can be replicated
974+ * in xref/.
975+ */
976+ ret .localRelPath = absolute .getParent ().relativize (
977+ canonical .toPath ()).toString ();
978+ return false ;
971979 }
972980
981+ /*
982+ * No need to synchronize access to acceptedNonlocalSymlinks, as
983+ * indexDown() runs on one thread.
984+ */
985+
973986 String absolute0 ;
974- // No need to synchronize, as indexDown() runs on one thread.
975987 if ((absolute0 = acceptedNonlocalSymlinks .get (canonical1 )) != null ) {
976988 if (absolute1 .equals (absolute0 )) {
977989 return true ;
978- } else if (!isCanonicalDir ) {
979- if (LOGGER .isLoggable (Level .FINE )) {
980- LOGGER .log (Level .FINE , "External file {0} has symlink from {1} after first {2}" ,
990+ }
991+ if (!isCanonicalDir ) {
992+ if (LOGGER .isLoggable (Level .FINEST )) {
993+ LOGGER .log (Level .FINEST ,
994+ "External file {0} has symlink from {1} after first {2}" ,
981995 new Object [] {canonical1 , absolute1 , absolute0 });
982996 }
983997 return true ;
984- } else {
998+ }
999+
1000+ /*
1001+ * Do not accept symlinks to external directories already accepted
1002+ * as linked elsewhere, because the canonical target will be
1003+ * indexed already -- but relativize() a path to be returned in ret
1004+ * so that this second symlink can be redone as a local
1005+ * (non-external) symlink in xref/.
1006+ */
1007+ ret .localRelPath = absolute .getParent ().relativize (
1008+ Paths .get (absolute0 )).toString ();
1009+
1010+ if (LOGGER .isLoggable (Level .FINEST )) {
1011+ LOGGER .log (Level .FINEST , "External dir {0} has symlink from {1} after first {2}" ,
1012+ new Object [] {canonical1 , absolute1 , absolute0 });
1013+ }
1014+ return false ;
1015+ }
1016+
1017+ /*
1018+ * Iterate through acceptedNonlocalSymlinks, which is sorted so that
1019+ * longer canonical entries come first, to see if the new link is a
1020+ * child canonically.
1021+ */
1022+ for (String canonicalPrev : acceptedNonlocalSymlinks .keySet ()) {
1023+ if (canonical1 .startsWith (canonicalPrev + File .separator )) {
1024+ String absolutePrev = acceptedNonlocalSymlinks .get (canonicalPrev );
1025+ if (!isCanonicalDir ) {
1026+ if (LOGGER .isLoggable (Level .FINEST )) {
1027+ LOGGER .log (Level .FINEST ,
1028+ "External file {0} has symlink from {1} under previous {2}" ,
1029+ new Object [] {canonical1 , absolute1 , absolutePrev });
1030+ }
1031+ return true ;
1032+ }
1033+
9851034 /*
986- * Do not accept symlinks to external directories already
987- * accepted as linked elsewhere, because the canonical target
988- * will be indexed already -- but relativize() a path to be
989- * returned in ret so that this second symlink can be redone as
990- * a local (non-external) symlink in xref/.
1035+ * See above about redoing a sourceRoot symlink as a local
1036+ * (non-external) symlink in xref/.
9911037 */
992- ret .localRelPath = absolute .getParent ().relativize (
993- Paths .get (absolute0 )).toString ();
1038+ Path abs0 = Paths .get (absolutePrev , canonical1 .substring (
1039+ canonicalPrev .length () + 1 ));
1040+ ret .localRelPath = absolute .getParent ().relativize (abs0 ).toString ();
9941041
995- if (LOGGER .isLoggable (Level .FINE )) {
996- LOGGER .log (Level .FINE , "External dir {0} has symlink from {1} after first {2}" ,
997- new Object [] {canonical1 , absolute1 , absolute0 });
1042+ if (LOGGER .isLoggable (Level .FINEST )) {
1043+ LOGGER .log (Level .FINEST ,
1044+ "External dir {0} has symlink from {1} under previous {2}" ,
1045+ new Object [] {canonical1 , absolute1 , absolutePrev });
9981046 }
9991047 return false ;
10001048 }
@@ -1005,8 +1053,8 @@ private boolean acceptSymlink(Path absolute, File canonical, AcceptSymlinkRet re
10051053 Set <String > canonicalRoots = env .getCanonicalRoots ();
10061054 for (String canonicalRoot : canonicalRoots ) {
10071055 if (canonical1 .startsWith (canonicalRoot )) {
1008- if (LOGGER .isLoggable (Level .FINE )) {
1009- LOGGER .log (Level .FINE , "Allowed symlink {0} per canonical root {1}" ,
1056+ if (LOGGER .isLoggable (Level .FINEST )) {
1057+ LOGGER .log (Level .FINEST , "Allowed symlink {0} per canonical root {1}" ,
10101058 new Object [] {absolute1 , canonical1 });
10111059 }
10121060 acceptedNonlocalSymlinks .put (canonical1 , absolute1 );
0 commit comments