1313 *******************************************************************************/
1414package org .eclipse .pde .internal .core ;
1515
16+ import java .io .BufferedInputStream ;
17+ import java .io .BufferedOutputStream ;
18+ import java .io .File ;
19+ import java .io .FileInputStream ;
20+ import java .io .FileNotFoundException ;
21+ import java .io .FileOutputStream ;
22+ import java .io .InputStream ;
1623import java .util .ArrayList ;
1724import java .util .Arrays ;
1825import java .util .Collection ;
2330import java .util .List ;
2431import java .util .Map ;
2532import java .util .Map .Entry ;
33+ import java .util .Objects ;
2634import java .util .Queue ;
2735import java .util .concurrent .ConcurrentLinkedQueue ;
2836import java .util .regex .Pattern ;
3240import org .eclipse .core .resources .IFile ;
3341import org .eclipse .core .resources .IProject ;
3442import org .eclipse .core .resources .IResource ;
43+ import org .eclipse .core .resources .IResourceChangeEvent ;
44+ import org .eclipse .core .resources .IResourceChangeListener ;
3545import org .eclipse .core .resources .ResourcesPlugin ;
3646import org .eclipse .core .runtime .CoreException ;
3747import org .eclipse .core .runtime .IPath ;
3848import org .eclipse .core .runtime .IProgressMonitor ;
3949import org .eclipse .core .runtime .IStatus ;
4050import org .eclipse .core .runtime .MultiStatus ;
4151import org .eclipse .core .runtime .Status ;
52+ import org .eclipse .core .runtime .SubMonitor ;
4253import org .eclipse .core .runtime .jobs .Job ;
4354import org .eclipse .jdt .core .IClasspathAttribute ;
4455import org .eclipse .jdt .core .IClasspathContainer ;
6273import org .eclipse .pde .internal .core .natures .PluginProject ;
6374import org .eclipse .pde .internal .core .project .PDEProject ;
6475import org .eclipse .pde .internal .core .util .CoreUtility ;
76+ import org .eclipse .pde .internal .core .util .PDEClasspathContainerSaveHelper ;
6577import org .eclipse .team .core .RepositoryProvider ;
6678
6779public class ClasspathComputer {
@@ -81,6 +93,20 @@ private record ClasspathConfiguration(IPluginModelBase model, IJavaProject javaP
8193 */
8294 private static final UpdateClasspathsJob fUpdateJob = new UpdateClasspathsJob ();
8395
96+ static final IResourceChangeListener CHANGE_LISTENER = new IResourceChangeListener () {
97+
98+ @ Override
99+ public void resourceChanged (IResourceChangeEvent event ) {
100+ IResource resource = event .getResource ();
101+ if (resource instanceof IProject project ) {
102+ if (PDECore .DEBUG_STATE ) {
103+ System .out .println (String .format ("Project %s was deleted." , project .getName ())); //$NON-NLS-1$
104+ }
105+ getStateFile (project ).delete ();
106+ }
107+ }
108+ };
109+
84110 public static void setClasspath (IProject project , IPluginModelBase model ) throws CoreException {
85111 IClasspathEntry [] entries = getClasspath (project , model , null , false , true );
86112 JavaCore .create (project ).setRawClasspath (entries , null );
@@ -508,14 +534,19 @@ public static void requestClasspathUpdate(Collection<IProject> updateProjects) {
508534 fUpdateJob .addAll (updateProjects );
509535 }
510536
537+ static void requestClasspathUpdate (IProject project , IClasspathContainer savedState ) {
538+ fUpdateJob .add (project , savedState );
539+ }
540+
511541 /**
512542 * Job to update class path containers asynchronously. Avoids blocking the
513543 * UI thread. The job is given a workspace lock so other jobs can't run on a
514544 * stale classpath.
515545 */
516546 private static final class UpdateClasspathsJob extends Job {
517547
518- private final Queue <IProject > workQueue = new ConcurrentLinkedQueue <>();
548+ private static final int WORK = 10_000 ;
549+ private final Queue <UpdateRequest > workQueue = new ConcurrentLinkedQueue <>();
519550
520551 /**
521552 * Constructs a new job.
@@ -533,29 +564,32 @@ public boolean belongsTo(Object family) {
533564 }
534565
535566 @ Override
536- protected IStatus run (IProgressMonitor monitor ) {
567+ protected IStatus run (IProgressMonitor jobMonitor ) {
568+ SubMonitor monitor = SubMonitor .convert (jobMonitor , PDECoreMessages .PluginModelManager_1 , WORK );
569+ PluginModelManager .getInstance ().initialize (monitor .split (10 ));
537570 PluginModelManager modelManager = PluginModelManager .getInstance ();
538571 Map <IJavaProject , IClasspathContainer > updateProjects = new LinkedHashMap <>();
539572 Map <IProject , IStatus > errorsPerProject = new LinkedHashMap <>();
540- IProject project ;
541- while (!monitor .isCanceled () && (project = workQueue .poll ()) != null ) {
573+ UpdateRequest request ;
574+ while (!monitor .isCanceled () && (request = workQueue .poll ()) != null ) {
575+ monitor .setWorkRemaining (WORK );
576+ IProject project = request .project ();
542577 if (project .exists () && project .isOpen ()) {
543578 IPluginModelBase model = modelManager .findModel (project );
544579 if (model != null && PluginProject .isJavaProject (project )) {
545580 IJavaProject javaProject = JavaCore .create (project );
546581 RequiredPluginsClasspathContainer classpathContainer = new RequiredPluginsClasspathContainer (
547582 model , project );
548583 try {
549- // eager compute the entries as they will be
550- // needed soon, if any error occurs here we do
551- // not update the entry, all errors will be
552- // reported at the end of the update!
553- classpathContainer .computeEntries ();
554- updateProjects .put (javaProject , classpathContainer );
555- errorsPerProject .remove (project );
584+ if (!isUpToDate (project , classpathContainer .computeEntries (), request .container ())) {
585+ updateProjects .put (javaProject , classpathContainer );
586+ errorsPerProject .remove (project );
587+ saveState (project , classpathContainer );
588+ }
556589 } catch (CoreException e ) {
557590 errorsPerProject .put (project , e .getStatus ());
558591 }
592+ monitor .worked (1 );
559593 }
560594 }
561595 }
@@ -598,10 +632,92 @@ protected IStatus run(IProgressMonitor monitor) {
598632 * Queues more projects/containers.
599633 */
600634 void addAll (Collection <IProject > tocheck ) {
601- workQueue .addAll (tocheck );
635+ for (IProject project : tocheck ) {
636+ workQueue .add (new UpdateRequest (project , null ));
637+ }
602638 schedule ();
603639 }
604640
641+ void add (IProject project , IClasspathContainer classpathContainer ) {
642+ if (project == null ) {
643+ return ;
644+ }
645+ workQueue .add (new UpdateRequest (project , classpathContainer ));
646+ schedule ();
647+ }
648+
649+ }
650+
651+ private static boolean isUpToDate (IProject project , IClasspathEntry [] currentEntries ,
652+ IClasspathContainer previousClasspathContainer ) {
653+ if (previousClasspathContainer == null ) {
654+ if (PDECore .DEBUG_STATE ) {
655+ System .out
656+ .println (String .format ("%s need update because it has no state to compare" , project .getName ())); //$NON-NLS-1$
657+ }
658+ return false ;
659+ }
660+ IClasspathEntry [] previousEntries = previousClasspathContainer .getClasspathEntries ();
661+ if (previousEntries == null || previousEntries .length != currentEntries .length ) {
662+ if (PDECore .DEBUG_STATE ) {
663+ System .out .println (String .format ("%s need update because entries do not match in size!" , //$NON-NLS-1$
664+ project .getName ()));
665+ }
666+ return false ;
667+ }
668+ for (int i = 0 ; i < previousEntries .length ; i ++) {
669+ IClasspathEntry previous = previousEntries [i ];
670+ IClasspathEntry current = currentEntries [i ];
671+ if (!Objects .equals (current , previous )) {
672+ if (PDECore .DEBUG_STATE ) {
673+ System .out .println (
674+ String .format ("%s need update because entry at position %d is different:\n \t %s\n \t %s" , //$NON-NLS-1$
675+ project .getName (), i , current , previous ));
676+ }
677+ return false ;
678+ }
679+ }
680+ return true ;
681+ }
682+
683+ private static void saveState (IProject project , RequiredPluginsClasspathContainer classpathContainer ) {
684+ try {
685+ File stateFile = getStateFile (project );
686+ stateFile .getParentFile ().mkdirs ();
687+ try (BufferedOutputStream stream = new BufferedOutputStream (new FileOutputStream (stateFile ))) {
688+ PDEClasspathContainerSaveHelper .writeContainer (classpathContainer , stream );
689+ }
690+ } catch (Exception e ) {
691+ // can't write then...
692+ if (PDECore .DEBUG_STATE ) {
693+ System .err .println (String .format ("Writing project state for %s failed!" , project .getName ())); //$NON-NLS-1$
694+ e .printStackTrace ();
695+ }
696+ }
697+ }
698+
699+ static IClasspathContainer readState (IProject project ) {
700+ try {
701+ File stateFile = getStateFile (project );
702+ try (InputStream stream = new BufferedInputStream (new FileInputStream (stateFile ))) {
703+ return PDEClasspathContainerSaveHelper .readContainer (stream );
704+ }
705+ } catch (Exception e ) {
706+ if (PDECore .DEBUG_STATE && !(e instanceof FileNotFoundException )) {
707+ System .err .println (String .format ("Restoring project state for %s failed!" , project .getName ())); //$NON-NLS-1$
708+ e .printStackTrace ();
709+ }
710+ return null ;
711+ }
712+ }
713+
714+ private static File getStateFile (IProject project ) {
715+ return PDECore .getDefault ().getStateLocation ().append ("cpc" ).append (project .getName ()) //$NON-NLS-1$
716+ .toFile ();
717+ }
718+
719+ private static record UpdateRequest (IProject project , IClasspathContainer container ) {
720+
605721 }
606722
607723}
0 commit comments