patch to store classpath results between restarts to aid performance (FELIX-2595)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@996072 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/sigil/eclipse/ui/src/org/apache/felix/sigil/eclipse/ui/internal/classpath/SigilClassPathContainer.java b/sigil/eclipse/ui/src/org/apache/felix/sigil/eclipse/ui/internal/classpath/SigilClassPathContainer.java
index 802e9e8..baf9cea 100644
--- a/sigil/eclipse/ui/src/org/apache/felix/sigil/eclipse/ui/internal/classpath/SigilClassPathContainer.java
+++ b/sigil/eclipse/ui/src/org/apache/felix/sigil/eclipse/ui/internal/classpath/SigilClassPathContainer.java
@@ -19,15 +19,24 @@
 
 package org.apache.felix.sigil.eclipse.ui.internal.classpath;
 
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
 import org.apache.felix.sigil.eclipse.SigilCore;
 import org.apache.felix.sigil.eclipse.job.ThreadProgressMonitor;
 import org.apache.felix.sigil.eclipse.model.project.ISigilProjectModel;
+import org.apache.felix.sigil.eclipse.ui.SigilUI;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.Path;
 import org.eclipse.jdt.core.IClasspathContainer;
 import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaProject;
 
 /**
  * @author dave
@@ -36,9 +45,13 @@
 public class SigilClassPathContainer implements IClasspathContainer
 {
 
-    private IClasspathEntry[] entries;
-    private ISigilProjectModel sigil;
+    private static final String CLASSPATH = "classpath";
+    private static final String EMPTY = "empty";
 
+    private IClasspathEntry[] entries;
+    
+    private final ISigilProjectModel sigil;
+    
     public SigilClassPathContainer(ISigilProjectModel sigil)
     {
         this.sigil = sigil;
@@ -90,9 +103,15 @@
     {
         try
         {
-            IProgressMonitor monitor = ThreadProgressMonitor.getProgressMonitor();
             if (sigil.exists()) {
-                entries = sigil.findExternalClasspath(monitor).toArray(new IClasspathEntry[0]);
+                entries = getCachedClassPath(sigil);
+                
+                if ( entries == null ) {
+                    IProgressMonitor monitor = ThreadProgressMonitor.getProgressMonitor();
+                    entries = sigil.findExternalClasspath(monitor).toArray(new IClasspathEntry[0]);
+                    
+                    cacheClassPath(sigil, entries);
+                }
             }
         }
         catch (CoreException e)
@@ -107,4 +126,146 @@
             }
         }
     }
+    
+    static IClasspathEntry[] getCachedClassPath(ISigilProjectModel project) {
+        File f = getClassPathDir(project);
+        if ( f == null || !f.exists() ) return null;
+        
+        File[] entries = f.listFiles();
+        if ( entries == null || entries.length == 0 ) return null;
+        
+        ArrayList<IClasspathEntry> list = new ArrayList<IClasspathEntry>(entries.length);
+        for(File entry : entries ) {
+            if ( EMPTY.equals(entry.getName() ) ) {
+                list.clear();
+                break;
+            }
+            else {
+                try
+                {
+                    list.add(readClassPath(project.getJavaModel(), entry));
+                }
+                catch (IOException e)
+                {
+                    SigilCore.warn("Failed to read classpath entry " + entry, e);
+                }
+            }
+        }
+        return list.toArray(new IClasspathEntry[list.size()]);
+    }
+
+    private static File getClassPathDir(ISigilProjectModel project)
+    {
+        IPath loc = project.getProject().getWorkingLocation(SigilUI.PLUGIN_ID);
+        
+        if ( loc == null ) return null;
+        
+        loc = loc.append(CLASSPATH);
+        
+        return new File(loc.toOSString());
+    }
+
+    static void cacheClassPath(ISigilProjectModel project, IClasspathEntry[] entries)
+    {
+        File f = getClassPathDir(project);
+        
+        if ( f == null ) return;
+        
+        if ( !f.exists() ) {
+            if (!f.mkdirs()) {
+                SigilCore.warn("Failed to create temp working directory " + f);
+                return;
+            }
+        }
+        
+        try
+        {
+            if (entries.length == 0)
+            {
+                File empty = new File(f, EMPTY);
+                empty.createNewFile();
+            }
+            else {
+                int i = 0;
+                for(IClasspathEntry e : entries) {
+                    File entry = new File(f, Integer.toString(i++));
+                    writeClassPath(project.getJavaModel(), entry, e);
+                }
+            }
+        }
+        catch (IOException e)
+        {
+            SigilCore.warn("Failed to read write classpath entries", e);
+        }
+    }
+
+    /**
+     * @param sigil
+     */
+    static void flushCachedClassPath(ISigilProjectModel project)
+    {
+        File f = getClassPathDir(project);
+        
+        if ( f == null || !f.exists() ) return;
+        
+        File[] files = f.listFiles();
+        if ( files == null ) return;
+        
+        for (File entry : files) {
+            entry.delete();
+        }
+    }
+    
+    private static IClasspathEntry readClassPath(IJavaProject javaModel, File entry) throws IOException
+    {
+        FileInputStream in = new FileInputStream(entry);
+        
+        try
+        {
+            ByteArrayOutputStream buf = new ByteArrayOutputStream();
+            
+            byte[] b = new byte[1024];
+            
+            for(;;) {
+                int r = in.read(b);
+                if ( r == -1 ) break;
+                buf.write(b, 0, r);
+            }
+            
+            String enc = buf.toString();
+            return javaModel.decodeClasspathEntry(enc);
+        }
+        finally
+        {
+            try
+            {
+                in.close();
+            }
+            catch (IOException e)
+            {
+                SigilCore.warn("Failed to close stream to " + entry, e);
+            }
+        }
+    }
+
+    private static void writeClassPath(IJavaProject javaModel, File file, IClasspathEntry entry) throws IOException 
+    {
+        String enc = javaModel.encodeClasspathEntry(entry);
+        FileOutputStream out = new FileOutputStream(file);
+        try {
+            out.write(enc.getBytes());
+            out.flush();
+        }
+        finally {
+            try
+            {
+                out.close();
+            }
+            catch (IOException e)
+            {
+                SigilCore.warn("Failed to close stream to " + entry, e);
+            }            
+        }
+    }
+    
 }
diff --git a/sigil/eclipse/ui/src/org/apache/felix/sigil/eclipse/ui/internal/classpath/SigilClasspathContainerInitializer.java b/sigil/eclipse/ui/src/org/apache/felix/sigil/eclipse/ui/internal/classpath/SigilClasspathContainerInitializer.java
index 311454a..2216efa 100644
--- a/sigil/eclipse/ui/src/org/apache/felix/sigil/eclipse/ui/internal/classpath/SigilClasspathContainerInitializer.java
+++ b/sigil/eclipse/ui/src/org/apache/felix/sigil/eclipse/ui/internal/classpath/SigilClasspathContainerInitializer.java
@@ -46,6 +46,8 @@
         throws CoreException
     {
         ISigilProjectModel sigil = SigilCore.create(project.getProject());
+        
+        SigilClassPathContainer.flushCachedClassPath(sigil);
 
         IClasspathContainer sigilContainer = new SigilClassPathContainer(sigil);
 
@@ -53,15 +55,24 @@
 
         IClasspathContainer[] respectiveContainers = new IClasspathContainer[] { sigilContainer };
 
+        
+        JavaCore.setClasspathContainer(containerPath, affectedProjects,
+            respectiveContainers, getMonitor());
+    }
+
+    /**
+     * @return
+     */
+    private IProgressMonitor getMonitor()
+    {
         IProgressMonitor monitor = ThreadProgressMonitor.getProgressMonitor();
 
         if (monitor == null)
         {
             monitor = Job.getJobManager().createProgressGroup();
         }
-
-        JavaCore.setClasspathContainer(containerPath, affectedProjects,
-            respectiveContainers, monitor);
+        
+        return monitor;
     }
 
     @Override
@@ -76,10 +87,7 @@
 
         IClasspathContainer[] respectiveContainers = new IClasspathContainer[] { sigilContainer };
 
-        IProgressMonitor monitor = Job.getJobManager().createProgressGroup();
-
         JavaCore.setClasspathContainer(containerPath, affectedProjects,
-            respectiveContainers, monitor);
+            respectiveContainers, getMonitor());
     }
-
 }