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
021package org.granite.builder.util;
022
023import static org.granite.builder.GraniteBuilder.FLEX_BUILDER_ID;
024import static org.granite.builder.GraniteBuilder.GRANITE_BUILDER_ID;
025
026import java.io.File;
027import java.io.IOException;
028import java.net.URI;
029import java.net.URL;
030import java.util.ArrayList;
031import java.util.Arrays;
032import java.util.Comparator;
033import java.util.List;
034
035import org.eclipse.core.resources.ICommand;
036import org.eclipse.core.resources.IFile;
037import org.eclipse.core.resources.IProject;
038import org.eclipse.core.resources.IResource;
039import org.eclipse.core.resources.IWorkspace;
040import org.eclipse.core.resources.IWorkspaceRoot;
041import org.eclipse.core.runtime.CoreException;
042import org.eclipse.core.runtime.IPath;
043import org.eclipse.core.runtime.IStatus;
044import org.eclipse.core.runtime.Path;
045import org.eclipse.core.runtime.Status;
046import org.eclipse.jdt.core.IClasspathContainer;
047import org.eclipse.jdt.core.IClasspathEntry;
048import org.eclipse.jdt.core.IJavaElement;
049import org.eclipse.jdt.core.IJavaProject;
050import org.eclipse.jdt.core.JavaCore;
051import org.granite.builder.GraniteActivator;
052import org.granite.util.ClassUtil;
053
054/**
055 * @author Franck WOLFF
056 */
057public 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}