001    /*
002      GRANITE DATA SERVICES
003      Copyright (C) 2011 GRANITE DATA SERVICES S.A.S.
004    
005      This file is part of Granite Data Services.
006    
007      Granite Data Services is free software; you can redistribute it and/or modify
008      it under the terms of the GNU Library General Public License as published by
009      the Free Software Foundation; either version 2 of the License, or (at your
010      option) any later version.
011    
012      Granite Data Services is distributed in the hope that it will be useful, but
013      WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014      FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015      for more details.
016    
017      You should have received a copy of the GNU Library General Public License
018      along with this library; if not, see <http://www.gnu.org/licenses/>.
019    */
020    
021    package org.granite.builder.util;
022    
023    import static org.granite.builder.GraniteBuilder.FLEX_BUILDER_ID;
024    import static org.granite.builder.GraniteBuilder.GRANITE_BUILDER_ID;
025    
026    import java.io.File;
027    import java.io.IOException;
028    import java.net.URI;
029    import java.net.URL;
030    import java.util.ArrayList;
031    import java.util.Arrays;
032    import java.util.Comparator;
033    import java.util.List;
034    
035    import org.eclipse.core.resources.ICommand;
036    import org.eclipse.core.resources.IFile;
037    import org.eclipse.core.resources.IProject;
038    import org.eclipse.core.resources.IResource;
039    import org.eclipse.core.resources.IWorkspace;
040    import org.eclipse.core.resources.IWorkspaceRoot;
041    import org.eclipse.core.runtime.CoreException;
042    import org.eclipse.core.runtime.IPath;
043    import org.eclipse.core.runtime.IStatus;
044    import org.eclipse.core.runtime.Path;
045    import org.eclipse.core.runtime.Status;
046    import org.eclipse.jdt.core.IClasspathContainer;
047    import org.eclipse.jdt.core.IClasspathEntry;
048    import org.eclipse.jdt.core.IJavaElement;
049    import org.eclipse.jdt.core.IJavaProject;
050    import org.eclipse.jdt.core.JavaCore;
051    import org.granite.builder.GraniteActivator;
052    import org.granite.util.ClassUtil;
053    
054    /**
055     * @author Franck WOLFF
056     */
057    public class ProjectUtil {
058    
059            public static final Comparator<IPath> IPATH_COMPARATOR = new Comparator<IPath>() {
060                    @Override
061                    public int compare(IPath path1, IPath path2) {
062                            return path1.toString().compareTo(path2.toString());
063                    }
064            };
065            
066            public static IProject[] getAllProjects(IProject project) {
067                    if (project == null)
068                            return new IProject[0];
069                    return project.getWorkspace().getRoot().getProjects();
070            }
071            
072            public static IProject[] getAllOtherGraniteProjects(IProject project) throws CoreException {
073                    if (project == null)
074                            return new IProject[0];
075                    List<IProject> projects = new ArrayList<IProject>();
076                    for (IProject p : getAllProjects(project)) {
077                            if (!p.equals(project) && isGraniteProject(p))
078                                    projects.add(p);
079                    }
080                    return projects.toArray(new IProject[projects.size()]);
081            }
082            
083            public static IProject getProject(IProject project, String name) {
084                    if (project == null || name == null)
085                            return null;
086                    return project.getWorkspace().getRoot().getProject(name);
087            }
088            
089            public static boolean isGraniteProject(IProject project) throws CoreException {
090                    if (project != null && project.exists() && project.isOpen()) {
091                            for (ICommand command : project.getDescription().getBuildSpec()) {
092                                    if (GRANITE_BUILDER_ID.equals(command.getBuilderName()))
093                                            return true;
094                            }
095                    }
096                    return false;
097            }
098            
099            public static boolean isFlexBuilderProject(IProject project) throws CoreException {
100                    if (project != null && project.exists() && project.isOpen()) {
101                            for (ICommand command : project.getDescription().getBuildSpec()) {
102                                    if (FLEX_BUILDER_ID.equals(command.getBuilderName()))
103                                            return true;
104                            }
105                    }
106                    return false;
107            }
108    
109        public static List<IPath> makeRelative(List<IPath> paths) {
110            if (paths == null)
111                    return null;
112            return Arrays.asList(makeRelative(paths.toArray(new IPath[paths.size()])));
113        }
114        
115        public static IPath[] makeRelative(IPath[] paths) {
116            if (paths != null) {
117                    for (int i = 0; i < paths.length; i++)
118                            paths[i] = paths[i].makeRelative();
119            }
120            return paths;
121        }
122        
123        public static File getWorkspaceFile(IProject project) {
124            IWorkspace workspace = project.getWorkspace();
125            return workspace.getRoot().getLocation().toFile().getAbsoluteFile();
126        }
127        
128        public static URI getWorkspaceURI(IProject project) {
129            return getWorkspaceFile(project).toURI();
130        }
131        
132        public static File getProjectFile(IProject project) {
133            try {
134                            return FileUtil.getLocationFile(project).getAbsoluteFile();
135                    } catch (CoreException e) {
136                            throw new RuntimeException("Could not get " + project + " location file", e);
137                    }
138        }
139        
140        public static URI getProjectURI(IProject project) {
141            try {
142                            return FileUtil.getLocationURI(project);
143                    } catch (CoreException e) {
144                            throw new RuntimeException("Could not get " + project + " location URI", e);
145                    }
146        }
147        
148        public static List<CpEntry> getFullResolvedClasspath(IJavaProject project) throws CoreException {
149            return getFullClasspath(project, project.getResolvedClasspath(true));
150        }
151        
152        public static List<CpEntry> getFullClasspath(IJavaProject project) throws CoreException {
153            return getFullClasspath(project, project.getRawClasspath());
154        }
155            
156        public static List<CpEntry> getFullClasspath(IJavaProject project, IClasspathEntry[] classpathEntries) throws CoreException {
157            List<CpEntry> classpath = new ArrayList<CpEntry>();
158    
159            IWorkspaceRoot workspace = project.getProject().getWorkspace().getRoot();
160            IPath path = null;
161            try {
162    
163                    // Output locations.
164                    if (project.getOutputLocation() != null) {
165                    path = project.getOutputLocation();
166                    File file = workspace.findMember(path).getLocation().toFile();
167                    classpath.add(new CpEntry(path.makeRelative().toString(), path, file, CpEntry.CpeKind.PROJECT_OUTPUT_DIR));
168                    }
169                    for (IClasspathEntry cpe : classpathEntries) {
170                            if (cpe.getEntryKind() == IClasspathEntry.CPE_SOURCE && cpe.getOutputLocation() != null) {
171                        path = cpe.getOutputLocation();
172                        File file = workspace.findMember(path).getLocation().toFile();
173                        classpath.add(new CpEntry(path.makeRelative().toString(), path, file, CpEntry.CpeKind.SOURCE_OUTPUT_DIR));
174                            }
175                    }
176                    
177                    // Project jars.
178                for (IClasspathEntry cpe : classpathEntries) {
179                    if (cpe.getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
180                        path = cpe.getPath();
181                        if (path != null) {
182                            IResource member = workspace.findMember(path);
183                            String description = path.lastSegment();
184                                            if (path.segmentCount() > 1)
185                                                    description += " - " + path.removeLastSegments(1).makeRelative();
186                            if (member != null)
187                                    classpath.add(new CpEntry(description, path, member.getLocation().toFile(), CpEntry.CpeKind.LIB_JAR));
188                            else
189                                    classpath.add(new CpEntry(description, path, path.toFile(), CpEntry.CpeKind.LIB_JAR));
190                        }
191                    }
192                }
193                
194                // Containers jars/directories.
195                for (IClasspathEntry cpe : classpathEntries) {
196                    if (cpe.getEntryKind() == IClasspathEntry.CPE_CONTAINER) {
197                                    path = cpe.getPath();
198                                    IClasspathContainer container = JavaCore.getClasspathContainer(path, project);
199                                    String description = container.getDescription();
200                                    CpEntry entry = new CpEntry(description, path, (path != null ? path.toFile() : null), CpEntry.CpeKind.CONTAINER_JAR);
201                                    
202                                    for (IClasspathEntry ccpe : container.getClasspathEntries()) {
203                                            path = ccpe.getPath();
204                                            String label = path.lastSegment();
205                                            if (path.segmentCount() > 1)
206                                                    label += " - " + path.removeLastSegments(1).makeRelative();
207                                            
208                                            File file = path.toFile();
209                                            IResource resource = workspace.findMember(path);
210                                            if (resource != null)
211                                                    file = resource.getLocation().toFile();
212    
213                                            entry.getChildren().add(new CpEntry(label, path, file, CpEntry.CpeKind.LIB_JAR));
214                                    }
215                                    
216                                    classpath.add(entry);
217                    }
218                }
219                    
220            } catch (Exception e) {
221                String msg = "Problem with classpath location: " + path;
222                throw new CoreException(createErrorStatus(msg, e));
223            }
224            
225            return classpath;
226        }
227    
228        public static List<IPath> getSourceFolders(IJavaProject project) throws CoreException {
229            List<IPath> paths = new ArrayList<IPath>();
230    
231            for (IClasspathEntry cpe : project.getRawClasspath()) {
232                if (cpe.getEntryKind() == IClasspathEntry.CPE_SOURCE)
233                    paths.add(cpe.getPath());
234            }
235            
236            return paths;
237        }
238        
239        public static IPath getOutputFolder(IJavaProject project, IClasspathEntry cpe) throws CoreException {
240            if (cpe.getOutputLocation() != null)
241                    return cpe.getOutputLocation();
242            return project.getOutputLocation();
243        }
244        
245        public static List<IPath> getOutputFolders(IJavaProject project) throws CoreException {
246            List<IPath> paths = new ArrayList<IPath>();
247    
248            for (IClasspathEntry cpe : project.getRawClasspath()) {
249                if (cpe.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
250                    IPath output = cpe.getOutputLocation();
251                    if (output != null)
252                        paths.add(output);
253                }
254            }
255            IPath output = project.getOutputLocation();
256            if (output != null)
257                paths.add(output);
258            
259            return paths;
260        }
261        
262        public static IPath getJavaPath(IJavaProject project, IFile classFile, boolean keepInner) throws CoreException {
263            IPath classPath = classFile.getFullPath();
264            
265            List<IPath> outputFolders = getOutputFolders(project);
266            IPath classOutputFolder = null;
267            for (IPath outputFolder : outputFolders) {
268                    if (outputFolder.isPrefixOf(classPath)) {
269                            classOutputFolder = outputFolder;
270                            break;
271                    }
272            }
273            if (classOutputFolder == null)
274                    return null;
275            
276            classPath = classPath.removeFirstSegments(classOutputFolder.segmentCount());
277            
278            String sPath = classPath.toString();
279            sPath = sPath.substring(0, sPath.length() - classPath.getFileExtension().length() - 1);
280            if (!keepInner) {
281                    int iDollar = sPath.indexOf('$');
282                    if (iDollar != -1)
283                            sPath = sPath.substring(iDollar);
284            }
285            sPath += ".java";
286            
287            return new Path(sPath);
288        }
289    
290        public static JavaClassInfo getJavaClassInfo(IJavaProject project, Class<?> clazz) {
291            try {
292                    URI clazzUri = ClassUtil.findResource(clazz).toURI();
293                    URI projectUri = getProjectURI(project.getProject());
294                    URI relativeURI = projectUri.relativize(clazzUri);
295                    return getJavaClassInfo(project, project.getProject().getFile(relativeURI.toString()));
296            } catch (Exception e) {
297                    return null;
298            }
299        }
300        
301        public static JavaClassInfo getJavaClassInfo(IJavaProject project, IFile resource) throws CoreException {
302            // classPath = "<project name>/<output folder>/<package>/<file>.class"
303            IPath classPath = resource.getFullPath().makeRelative();
304            
305            // Find output folder: "<project name>/<output folder>".
306            IPath outputFolder = null;
307            if (project.getOutputLocation() != null && project.getOutputLocation().isPrefixOf(classPath))
308                    outputFolder = project.getOutputLocation();
309            else {
310                    for (IClasspathEntry cpe : project.getRawClasspath()) {
311                    if (cpe.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
312                        IPath output = cpe.getOutputLocation();
313                        if (output != null && output.isPrefixOf(classPath)) {
314                            outputFolder = output;
315                            break;
316                        }
317                    }
318                    }
319            }
320            
321            if (outputFolder == null)
322                    return null;
323            
324            // Compute class name.
325            IPath relativeClassPath = classPath.removeFirstSegments(outputFolder.segmentCount());
326            IPath relativeClassName = relativeClassPath.removeFileExtension();
327            String className = relativeClassName.toPortableString().replace('/', '.');
328            
329            // Compute java source path: "<package>/<class name>[$<inner class>].java".
330            String javaFullPath = relativeClassName.addFileExtension("java").toPortableString();
331            String javaFilePath = javaFullPath;
332            int iDollar = javaFilePath.indexOf('$');
333            if (iDollar != -1)
334                    javaFilePath = javaFilePath.substring(0, iDollar) + ".java";
335    
336            IJavaElement javaElement = project.findElement(new Path(javaFilePath));
337            if (javaElement == null)
338                    return null;
339    
340            IJavaElement sourceFolder = javaElement.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
341            if (sourceFolder == null)
342                    return null;
343            
344            IPath folderPath = sourceFolder.getPath().makeRelative();
345            if (folderPath.segmentCount() > 0)
346                    folderPath = folderPath.removeFirstSegments(1);
347            
348            // Fix issues with project not in the workspace directory.
349            outputFolder = project.getProject().getWorkspace().getRoot().findMember(outputFolder).getLocation();
350            
351            return new JavaClassInfo(
352                    folderPath.toPortableString(),
353                    javaFullPath,
354                    className,
355                    new File(outputFolder.toPortableString(), relativeClassPath.toPortableString())
356            );
357        }
358        
359        public static IStatus createErrorStatus(String message) {
360            return new Status(IStatus.ERROR, GraniteActivator.PLUGIN_ID, message);
361        }
362        
363        public static IStatus createErrorStatus(String message, Throwable t) {
364            return new Status(IStatus.ERROR, GraniteActivator.PLUGIN_ID, IStatus.OK, message, t);
365        }
366        
367        public static class CpEntry {
368            
369            public enum CpeKind {
370                    PROJECT_OUTPUT_DIR,
371                    SOURCE_OUTPUT_DIR,
372                    CONTAINER_JAR,
373                    LIB_JAR
374            }
375    
376            private final String description;
377            private final IPath path;
378            private final File file;
379            private final CpeKind kind;
380            private final List<CpEntry> children = new ArrayList<CpEntry>();
381    
382            public CpEntry(String description, IPath path, File file, CpeKind kind) {
383                    this.description = description;
384                            this.path = path;
385                            this.file = file;
386                            this.kind = kind;
387                    }
388    
389                    public String getDescription() {
390                            return description;
391                    }
392    
393                    public IPath getPath() {
394                            return path;
395                    }
396    
397                    public File getFile() {
398                            return file;
399                    }
400    
401                    public CpeKind getKind() {
402                            return kind;
403                    }
404                    
405                    public List<CpEntry> getChildren() {
406                            return children;
407                    }
408    
409                    public boolean exists() {
410                            return (file != null && file.exists());
411                    }
412                    
413                    public URL toURL() throws IOException {
414                            if (file == null)
415                                    return null;
416                            return file.getCanonicalFile().toURI().toURL();
417                    }
418    
419                    @Override
420                    public String toString() {
421                            return "[" + kind + "] " + path + " -> " + file;
422                    }
423        }
424    }