/*******************************************************************************
* Copyright (c) 2011 - 2015 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.common.web;
import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectNature;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.wst.common.componentcore.ComponentCore;
import org.eclipse.wst.common.componentcore.internal.util.IModuleConstants;
import org.eclipse.wst.common.componentcore.resources.ITaggedVirtualResource;
import org.eclipse.wst.common.componentcore.resources.IVirtualComponent;
import org.eclipse.wst.common.componentcore.resources.IVirtualFolder;
import org.eclipse.wst.common.project.facet.core.IFacetedProject;
import org.eclipse.wst.common.project.facet.core.IProjectFacet;
import org.eclipse.wst.common.project.facet.core.ProjectFacetsManager;
import org.jboss.tools.common.core.CommonCorePlugin;
import org.jboss.tools.common.core.Messages;
public class WebUtils {
public static IPath[] getWebInfPaths(IProject project) {
IContainer[] cs = getWebRootFolders(project, true);
List<IPath> ps = new ArrayList<IPath>();
for (IContainer c: cs) {
IFolder f = c.getFolder(new Path("/WEB-INF")); //$NON-NLS-1$
if(f.exists()) {
ps.add(f.getFullPath());
}
}
return ps.toArray(new IPath[ps.size()]);
}
public static IPath[] getWebContentPaths(IProject project) {
IContainer[] cs = getWebRootFolders(project, true);
IPath[] ps = new IPath[cs.length];
for (int i = 0; i < cs.length; i++) {
ps[i] = cs[i].getFullPath();
}
return ps;
}
public static IPath getFirstWebContentPath(IProject project) {
IContainer[] cs = getWebRootFolders(project, true);
for (IContainer c: cs) {
return c.getFullPath();
}
return null;
}
/**
* Returns the webroot folder which contain the given resource or null of the resource does not belong to any webroot.
* @param resource
* @return
*/
public static IContainer getWebRootFolder(IResource resource) {
IProject project = resource.getProject();
if(project!=null) {
IContainer[] cs = getWebRootFolders(project, false);
IPath fullPath = resource.getFullPath();
for (IContainer c: cs) {
if(c.getFullPath().isPrefixOf(fullPath)) {
return c;
}
}
}
return null;
}
/**
* Returns the webroot folders for a given project.
*
* @param project
* @param ignoreDerived
* @return IContainer[] array filled with a Roots or an empty array
*/
public static IContainer[] getWebRootFolders(IProject project, boolean ignoreDerived) {
IFacetedProject facetedProject = null;
try {
facetedProject = ProjectFacetsManager.create(project);
} catch (CoreException e) {
CommonCorePlugin.getDefault().logError(e);
}
IProjectFacet DYNAMIC_WEB_FACET = ProjectFacetsManager.getProjectFacet(IModuleConstants.JST_WEB_MODULE);
if(facetedProject!=null && facetedProject.getProjectFacetVersion(DYNAMIC_WEB_FACET)!=null) {
IVirtualComponent component = ComponentCore.createComponent(project);
if(component!=null) {
IVirtualFolder webRootVirtFolder = component.getRootFolder().getFolder(new Path("/")); //$NON-NLS-1$
IPath defaultPath = getDefaultDeploymentDescriptorFolder(webRootVirtFolder);
IContainer defaultResource = null;
IContainer[] folders = webRootVirtFolder.getUnderlyingFolders();
if(folders.length > 1){
ArrayList<IContainer> containers = new ArrayList<IContainer>();
for(IContainer container : folders){
if(container.exists() && (!ignoreDerived || !container.isDerived(IResource.CHECK_ANCESTORS))) {
String path = "/" + container.getProjectRelativePath().toString();
if(defaultPath!=null && path.equals(defaultPath.toString())) {
defaultResource = container;
} else {
containers.add(container);
}
}
}
if(defaultResource!=null) {
containers.add(0, defaultResource); // Put default root folder to the first position of the list
}
return containers.toArray(new IContainer[containers.size()]);
} else {
return folders;
}
}
} else {
IFolder www = getCordovaWebRootFolder(project);
if(www!=null && (!ignoreDerived || !www.isDerived(IResource.CHECK_ANCESTORS))) {
return new IContainer[] {www};
}
}
return EMPTY_ARRAY;
}
private static final String THYM_NATURE_ID = "org.eclipse.thym.core.HybridAppNature";
private static final String AEROGEAR_NATURE_ID = "org.jboss.tools.aerogear.hybrid.core.HybridAppNature";
private static final String CONFIG_XML = "config.xml";
/**
* Returns "www" folder of the project if this folder exists and the project is a cordova project.
* The project is recognized as a cordova project if it has corresponding nature or config.xml file.
* @param project
* @return
*/
private static IFolder getCordovaWebRootFolder(IProject project) {
IFolder webroot = null;
IResource www = project.findMember("www");
if(www !=null && www.getType() == IResource.FOLDER) {
webroot = (IFolder)www;
try {
if(project.hasNature(THYM_NATURE_ID) || project.hasNature(AEROGEAR_NATURE_ID)) {
IResource configXml = project.findMember(CONFIG_XML);
if(configXml ==null || configXml.getType() != IResource.FILE) {
configXml = webroot.findMember(CONFIG_XML);
if(configXml ==null || configXml.getType() != IResource.FILE) {
webroot = null;
}
}
}
} catch (CoreException e) {
CommonCorePlugin.getDefault().logError(e);
}
}
return webroot;
}
/**
* Finds a page resource in the project. If project is not a WTP one, the method uses
* context resource as a base to search the valid resource for a given path.
*
* For example, we have a project that has the following structure:
*
* aProject
* -- /www
* ----/css
* ----/some.css
* ----/pages
* ----index.html
*
* Given such a project that has WTP nature in its set up, the following call:
*
* WebUtils.findResource(</www/index.html IFile object>, "/css/some.css");
*
* will use WTP Roots to search a resource for some.css file.
*
* The same call for such a project that has no WTP nature set up will use '/www/index.html' file
* as a base to search for required CSS Stylesheet. So, it will search in
* - /www/pages/css/ - there is no some.css here, so the method will continue to search in parent folder
* - /www/css/ - there is 'some.css' file in this folder, so it will be returned as a result
*
* @param context - Context resource (might be a current page or a folder resource)
* @param filePath - Path to a file to search
* @return IResource for a given path in a project or null if resource cannot be found
*/
public static IResource findResource (IResource context, String filePath) {
if (filePath == null)
throw new IllegalArgumentException(MessageFormat.format(
Messages.WebUtil_NullArgument, "filePath"));
if (context == null)
throw new IllegalArgumentException(MessageFormat.format(
Messages.WebUtil_NullArgument, "context"));
IProject project = context.getProject();
// Check if the full absolute file path is provided
IPath tmpPath = new Path(filePath);
if (tmpPath.isAbsolute() && project.getLocation().isPrefixOf(tmpPath)) {
IFile file = project.getFile(tmpPath.removeFirstSegments(project.getLocation().segmentCount()));
if (file != null && file.exists()) {
return file;
}
}
IPath[] webContentPaths = WebUtils.getWebContentPaths(project);
if (tmpPath.isAbsolute()) { // Absolute Path
if (webContentPaths.length > 0) { // WTP Project
for(IPath webContentPath : webContentPaths) {
IPath container = webContentPath.segmentCount() > 1 ?
webContentPath.removeFirstSegments(1) : project.getFullPath();
IFile file = project.getFile(container.append(filePath));
if(file.exists())
return file;
}
} else { // Non-WTP Project
IContainer parent = context instanceof IContainer ? (IContainer)context : context.getParent();
IPath path = new Path(filePath).makeRelative();
while (parent != null) {
IFile file = parent.getFile(path);
if (file.exists())
return file;
parent = parent.getParent();
}
}
} else {
if (webContentPaths.length > 0) {
IContainer contextRoot = getWebRootFolder(context);
if (contextRoot == null)
return null;
IContainer parent = context instanceof IContainer ? (IContainer)context : context.getParent();
File contextFile = parent.getLocation().toFile();
String contextRelatedFileName = null;
try {
contextRelatedFileName = new File(contextFile.toString() + File.separator + filePath).getCanonicalPath();
} catch (IOException e) {
return null;
}
IPath contextRelatedPath = new Path(contextRelatedFileName);
IPath webRootRelatedPath = contextRelatedPath.removeFirstSegments(contextRoot.getLocation().segmentCount());
for (IPath webContentPath : webContentPaths) {
IFile file = project.getFile(webContentPath.removeFirstSegments(1).append(webRootRelatedPath));
if (file.exists())
return file;
}
} else {
IFile file = project.getFile(context.getProjectRelativePath()
.removeLastSegments(1).append(filePath));
if (file.exists())
return file;
}
}
return null;
}
/**
* Returns the path of the resource.
* If "context" is not null and belongs to the same project as "resource" then this method returns the path of "resource" relative to "context".
* For example:
* context = "<project1>/web/html/index.html"
* resource = "<project1>/web/img/pic.gif"
* getWebPath(context, resource, true) will return "../img/pic.gif";
* Otherwise the path of the resource relative to the web root folder is returned. If the resource doesn't belong to any web root then the project relative path is returned.
* For example: "/img/pic.gif".
*
* @param context
* @param resource
* @return
*/
public static String getWebPath(IFile context, IFile resource) {
String path;
if(context==null || !context.getProject().equals(resource.getProject())) {
IContainer root = getWebRootFolder(resource);
if(root==null) {
path = resource.getProjectRelativePath().toString();
} else {
path = resource.getFullPath().removeFirstSegments(root.getFullPath().segmentCount()).toString();
}
if(!path.startsWith("/")) {
return "/" + path;
}
} else {
path = resource.getFullPath().makeRelativeTo(context.getParent().getFullPath()).toString();
}
return path;
}
private static final IContainer[] EMPTY_ARRAY = new IContainer[0];
public static final String DD_FOLDER_TAG = org.eclipse.wst.common.componentcore.internal.WorkbenchComponent.DEFAULT_ROOT_SOURCE_TAG;
/**
* Returns all the web root folders of the project.
* If the project is not a web project then the method will return an empty array.
* All the derived resources or resources belonged to derived containers will be eliminated.
* If some folder is set as default web root source folder (available since WTP 3.3.1) then this folder will be places in the very beginning of the result array.
* @param project
* @return
*/
public static IPath getDefaultDeploymentDescriptorFolder(IVirtualFolder folder) {
IPath returnValue = null;
if (folder instanceof ITaggedVirtualResource){
returnValue = ((ITaggedVirtualResource)folder).getFirstTaggedResource(DD_FOLDER_TAG);
}
return returnValue;
}
}