Java tutorial
/******************************************************************************* * Copyright 2011 Google Inc. All Rights Reserved. * * All rights reserved. This program and the accompanying materials * are 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 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ package com.google.gdt.eclipse.designer.util; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.gdt.eclipse.designer.Activator; import com.google.gdt.eclipse.designer.IExceptionConstants; import com.google.gdt.eclipse.designer.common.Constants; import com.google.gdt.eclipse.designer.model.module.GwtDocumentHandler; import com.google.gdt.eclipse.designer.model.module.ModuleElement; import com.google.gdt.eclipse.designer.model.module.ScriptElement; import com.google.gdt.eclipse.designer.model.module.SetPropertyFallbackElement; import com.google.gdt.eclipse.designer.model.module.StylesheetElement; import com.google.gdt.eclipse.designer.model.web.WebAppElement; import com.google.gdt.eclipse.designer.model.web.WebDocumentEditContext; import com.google.gdt.eclipse.designer.model.web.WebUtils; import com.google.gdt.eclipse.designer.model.web.WelcomeFileElement; import com.google.gdt.eclipse.designer.model.web.WelcomeFileListElement; import org.eclipse.wb.internal.core.utils.IOUtils2; import org.eclipse.wb.internal.core.utils.Version; import org.eclipse.wb.internal.core.utils.exception.DesignerException; import org.eclipse.wb.internal.core.utils.execution.ExecutionUtils; import org.eclipse.wb.internal.core.utils.execution.RunnableObjectEx; import org.eclipse.wb.internal.core.utils.external.ExternalFactoriesHelper; import org.eclipse.wb.internal.core.utils.jdt.core.CodeUtils; import org.eclipse.wb.internal.core.utils.jdt.core.ProjectUtils; import org.eclipse.wb.internal.core.utils.reflect.ProjectClassLoader; import org.eclipse.wb.internal.core.utils.xml.parser.QParser; 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.IResource; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.QualifiedName; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaModel; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.CompilationUnit; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.io.input.XmlStreamReader; import org.apache.commons.lang.StringUtils; import org.htmlparser.Node; import org.htmlparser.Parser; import org.htmlparser.lexer.Lexer; import org.htmlparser.lexer.Page; import org.htmlparser.nodes.TagNode; import org.htmlparser.util.DefaultParserFeedback; import org.htmlparser.visitors.TagFindingVisitor; import java.io.ByteArrayInputStream; import java.io.File; import java.io.InputStream; import java.io.Reader; import java.io.StringReader; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.jar.JarFile; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Various non UI utilities for GWT. * * @author scheglov_ke * @coverage gwt.util */ public final class Utils { //////////////////////////////////////////////////////////////////////////// // // Environment // //////////////////////////////////////////////////////////////////////////// /** * @return <code>true</code> if Eclipse has GPE - Google Plugin for Eclipse, installed. */ public static boolean hasGPE() { if (System.getProperty("wbp.noGPE") != null) { return false; } return Platform.getBundle("com.google.gwt.eclipse.core") != null; } //////////////////////////////////////////////////////////////////////////// // // Libraries // //////////////////////////////////////////////////////////////////////////// /** * Get absolute path to the <code>GWT_HOME</code>. * * @param project * optional GWT {@link IProject}, if not <code>null</code>, then project-specific * gwt-user.jar may be returned; if <code>null</code>, then workspace-global one. */ public static String getGWTLocation(IProject project) { IPath userLibPath = getUserLibPath(project); if (userLibPath != null) { return userLibPath.removeLastSegments(1).toPortableString(); } else { return null; } } /** * Get absolute path to the gwt-user.jar. * * @param project * optional GWT {@link IProject}, if not <code>null</code>, then project-specific * gwt-user.jar may be returned; if <code>null</code>, then workspace-global one. */ public static IPath getUserLibPath(final IProject project) { // when no project, use workspace-global GWT_HOME if (project == null) { return new Path(Activator.getGWTLocation()).append("gwt-user.jar"); } // try to find project-specific GWT location return ExecutionUtils.runObject(new RunnableObjectEx<IPath>() { public IPath runObject() throws Exception { IJavaProject javaProject = JavaCore.create(project); String[] entries = ProjectClassLoader.getClasspath(javaProject); // try to find gwt-user.jar by name String userJarEntry = getUserJarEntry(entries); if (userJarEntry != null) { return new Path(userJarEntry); } // try to find gwt-user.jar by contents for (String entry : entries) { if (entry.endsWith(".jar")) { JarFile jarFile = new JarFile(entry); try { if (jarFile.getEntry("com/google/gwt/core/Core.gwt.xml") != null) { return new Path(entry); } } finally { jarFile.close(); } } } // not found return null; } }); } static String getUserJarEntry(String[] entries) { for (String entry : entries) { // lookup for gwt-user.jar entry; it can be 'gwt-user-1.5.3.jar', so use regexp Pattern p = Pattern.compile(".*gwt-user.*\\.jar"); Matcher m = p.matcher(entry); if (m.matches()) { return entry; } } return null; } /** * Get absolute path to the gwt-dev-windows.jar or gwt-dev-linux.jar * * @param project * optional GWT {@link IProject}, if not <code>null</code>, then project-specific * gwt-user.jar may be returned; if <code>null</code>, then workspace-global one. */ public static IPath getDevLibPath(IProject project) { // try to use location of gwt-user.jar { String gwtLocation = getGWTLocation(project); // Maven if (gwtLocation.contains("/gwt/gwt-user/")) { String gwtFolder = StringUtils.substringBefore(gwtLocation, "/gwt-user/"); String versionString = StringUtils.substringAfter(gwtLocation, "/gwt/gwt-user/"); String devFolder = gwtFolder + "/gwt-dev/" + versionString; String devFileName = "gwt-dev-" + versionString + ".jar"; Path path = new Path(devFolder + "/" + devFileName); if (path.toFile().exists()) { return path; } } // gwt-dev in same folder as gwt-user.jar { IPath path = getDevLibPath(gwtLocation); if (path.toFile().exists()) { return path; } } } // use gwt-dev.jar from default GWT location { String gwtLocation = Activator.getGWTLocation(); return getDevLibPath(gwtLocation); } } private static IPath getDevLibPath(String gwtLocation) { String devLibName = "gwt-dev.jar"; return new Path(gwtLocation).append(devLibName); } //////////////////////////////////////////////////////////////////////////// // // Version // //////////////////////////////////////////////////////////////////////////// public static final Version GWT_2_0 = new Version(2, 0); public static final Version GWT_2_1 = new Version(2, 1); public static final Version GWT_2_1_1 = new Version(2, 1, 1); public static final Version GWT_2_2 = new Version(2, 2); public static final Version GWT_2_4 = new Version(2, 4); public static final Version GWT_2_5 = new Version(2, 5); /** * @return the default version of GWT, configured in preferences. */ public static Version getDefaultVersion() { String userLocation = Activator.getGWTLocation() + "/gwt-user.jar"; File userFile = new File(userLocation); if (userFile.exists()) { try { JarFile jarFile = new JarFile(userFile); try { if (hasClassEntry(jarFile, "com.google.gwt.user.cellview.client.RowHoverEvent")) { return GWT_2_5; } if (hasClassEntry(jarFile, "com.google.gwt.user.cellview.client.DataGrid")) { return GWT_2_4; } if (hasClassEntry(jarFile, "com.google.gwt.canvas.client.Canvas")) { return GWT_2_2; } if (hasClassEntry(jarFile, "com.google.gwt.user.client.ui.DirectionalTextHelper")) { return GWT_2_1_1; } if (hasClassEntry(jarFile, "com.google.gwt.cell.client.Cell")) { return GWT_2_1; } if (hasClassEntry(jarFile, "com.google.gwt.user.client.ui.LayoutPanel")) { return GWT_2_0; } } finally { jarFile.close(); } } catch (Throwable e) { } } // default version return GWT_2_4; } private static boolean hasClassEntry(JarFile jarFile, String className) { String path = className.replace('.', '/') + ".class"; return jarFile.getEntry(path) != null; } /** * Returns the version of GWT using in given {@link IProject}. * * @param javaProject * the GWT {@link IJavaProject}. */ public static Version getVersion(IProject project) { IJavaProject javaProject = JavaCore.create(project); return getVersion(javaProject); } /** * Returns the version of GWT using in given {@link IJavaProject}. * * @param javaProject * the GWT {@link IJavaProject}. */ public static Version getVersion(IJavaProject javaProject) { if (ProjectUtils.hasType(javaProject, "com.google.gwt.user.cellview.client.RowHoverEvent")) { return GWT_2_5; } if (ProjectUtils.hasType(javaProject, "com.google.gwt.user.cellview.client.DataGrid")) { return GWT_2_4; } if (ProjectUtils.hasType(javaProject, "com.google.gwt.canvas.client.Canvas")) { return GWT_2_2; } if (ProjectUtils.hasType(javaProject, "com.google.gwt.user.client.ui.DirectionalTextHelper")) { return GWT_2_1_1; } if (ProjectUtils.hasType(javaProject, "com.google.gwt.cell.client.Cell")) { return GWT_2_1; } if (ProjectUtils.hasType(javaProject, "com.google.gwt.user.client.ui.LayoutPanel")) { return GWT_2_0; } // default return GWT_2_4; } /** * @return <code>true</code> if given version of GWT contains MVP framework. */ public static boolean supportMvp(Version gwtVersion) { return gwtVersion.isHigherOrSame(GWT_2_1); } /** * @return <code>true</code> if given {@link IJavaProject} support MVP framework. */ public static boolean supportMvp(IJavaProject javaProject) { Version version = getVersion(javaProject); return supportMvp(version); } //////////////////////////////////////////////////////////////////////////// // // ModuleDescription utils // //////////////////////////////////////////////////////////////////////////// /** * @return <code>true</code> if given module inherits required, directly or indirectly. */ public static boolean inheritsModule(ModuleDescription moduleDescription, final String requiredModule) throws Exception { final AtomicBoolean result = new AtomicBoolean(); ModuleVisitor.accept(moduleDescription, new ModuleVisitor() { @Override public void endVisitModule(ModuleElement module) { if (requiredModule.equals(module.getId())) { result.set(true); } } }); return result.get(); } //////////////////////////////////////////////////////////////////////////// // // Single module access // //////////////////////////////////////////////////////////////////////////// /** * @return the {@link ModuleDescription} to which belongs given {@link ICompilationUnit}. */ public static ModuleDescription getSingleModule(ICompilationUnit unit) throws Exception { IPackageFragment packageFragment = (IPackageFragment) unit.getParent(); return getSingleModule(packageFragment); } /** * @return the {@link ModuleDescription} to which belongs given {@link IType}. */ public static ModuleDescription getSingleModule(IType type) throws Exception { IPackageFragment packageFragment = type.getPackageFragment(); return getSingleModule(packageFragment); } /** * @return the {@link ModuleDescription} to which belongs given {@link IPackageFragment}. */ public static ModuleDescription getSingleModule(IPackageFragment pkg) throws Exception { IResource packageResource = pkg.getUnderlyingResource(); return getSingleModule(packageResource); } /** * @return the {@link ModuleDescription} to which belongs given {@link IResource}, may be * <code>null</code> if no module found. */ public static ModuleDescription getSingleModule(IResource resource) throws Exception { List<ModuleDescription> modules = getModules(resource); // try to find marker for (ModuleDescription module : modules) { String content = IOUtils2.readString(module.getContents()); if (content.contains("gwtd.module.use")) { return module; } } // apply filters for (IModuleFilter filter : getModuleFilters()) { modules = filter.filter(modules); } // use first return modules.isEmpty() ? null : modules.get(0); } //////////////////////////////////////////////////////////////////////////// // // Module files searching // //////////////////////////////////////////////////////////////////////////// /** * @return the {@link ModuleDescription} that exactly corresponds to the given object, may be * <code>null</code> there are no corresponding module or more than one module exists. */ public static ModuleDescription getExactModule(Object object) { for (IModuleProvider moduleProvider : getModuleProviders()) { ModuleDescription module = moduleProvider.getExactModule(object); if (module != null) { return module; } } return null; } /** * @return the {@link ModuleDescription} with given id, may be <code>null</code> */ public static ModuleDescription getModule(IJavaProject javaProject, String id) throws Exception { for (IModuleProvider moduleProvider : getModuleProviders()) { ModuleDescription module = moduleProvider.getModuleDescription(javaProject, id); if (module != null) { return module; } } return null; } /** * @return all {@link ModuleDescription} in given {@link IJavaProject}. */ public static List<ModuleDescription> getModules(IJavaProject javaProject) throws Exception { List<ModuleDescription> modules = Lists.newArrayList(); for (IModuleProvider moduleProvider : getModuleProviders()) { modules.addAll(moduleProvider.getModules(javaProject)); } return modules; } /** * @return the {@link ModuleDescription}s to which belongs given {@link IResource}, may be empty * list if no module found. */ public static List<ModuleDescription> getModules(IResource resource) throws Exception { List<ModuleDescription> modules = Lists.newArrayList(); for (IModuleProvider moduleProvider : getModuleProviders()) { modules.addAll(moduleProvider.getModules(resource)); } return modules; } private static List<IModuleProvider> getModuleProviders() { return ExternalFactoriesHelper.getElementsInstances(IModuleProvider.class, "com.google.gdt.eclipse.designer.moduleProviders", "provider"); } private static List<IModuleFilter> getModuleFilters() { return ExternalFactoriesHelper.getElementsInstances(IModuleFilter.class, "com.google.gdt.eclipse.designer.moduleProviders", "filter"); } //////////////////////////////////////////////////////////////////////////// // // Module packages/folders access // //////////////////////////////////////////////////////////////////////////// /** * Accepts some {@link IPackageFragment} in "source" package and returns root "source" package. */ public static IPackageFragment getRootSourcePackage(IPackageFragment pkg) throws Exception { IPackageFragmentRoot sourceRoot = (IPackageFragmentRoot) pkg.getParent(); IPackageFragment clientRoot = pkg; while (isModuleSourcePackage(pkg)) { clientRoot = pkg; String pkgName = pkg.getElementName(); String pkgParentName = CodeUtils.getPackage(pkgName); pkg = sourceRoot.getPackageFragment(pkgParentName); } return clientRoot; } /** * @return <code>true</code> if given {@link IPackageFragment} is "source" package of some GWT * module. */ public static boolean isModuleSourcePackage(IPackageFragment packageFragment) throws Exception { final String packageName = packageFragment.getElementName(); // check enclosing module ModuleDescription module = getSingleModule(packageFragment); if (module != null) { final AtomicBoolean result = new AtomicBoolean(); ModuleVisitor.accept(module, new ModuleVisitor() { @Override public boolean visitModule(ModuleElement moduleElement) { String modulePackage = CodeUtils.getPackage(moduleElement.getId()) + "."; if (packageName.startsWith(modulePackage)) { String folderInModule = packageName.substring(modulePackage.length()).replace('.', '/'); if (moduleElement.isInSourceFolder(folderInModule)) { result.set(true); return false; } } return true; } }); return result.get(); } // no enclosing module return false; } //////////////////////////////////////////////////////////////////////////// // // Module reading // //////////////////////////////////////////////////////////////////////////// private static final Map<String, ModuleElement> m_readModule = Maps.newTreeMap(); /** * Reads module definition from given {@link ModuleDescription}. */ public static ModuleElement readModule(ModuleDescription moduleDescription) throws Exception { String id = moduleDescription.getId(); InputStream contents = moduleDescription.getContents(); return readModule(id, contents); } /** * Reads module definition from given stream. */ public static ModuleElement readModule(String id, InputStream inputStream) throws Exception { // read content, with correct encoding String contents; { Reader reader = new XmlStreamReader(inputStream); contents = IOUtils2.readString(reader); } // check cache String key = id + "|" + contents; { ModuleElement moduleElement = m_readModule.get(key); if (moduleElement != null) { return moduleElement; } } // parse using GWTDocumentHandler GwtDocumentHandler documentHandler = new GwtDocumentHandler(); try { QParser.parse(new StringReader(contents), documentHandler); } catch (Throwable e) { throw new DesignerException(IExceptionConstants.INVALID_MODULE_FILE, id); } // prepare module element ModuleElement moduleElement = documentHandler.getModuleElement(); moduleElement.setId(id); moduleElement.finalizeLoading(); // fill cache m_readModule.put(key, moduleElement); // done return moduleElement; } //////////////////////////////////////////////////////////////////////////// // // Resources // //////////////////////////////////////////////////////////////////////////// /** * @param moduleFile * the module *.gwt.xml file. * @param resource * the path of "public" resource. * * @return the existing resource {@link IFile}, may be <code>null</code>. */ public static IFile getFileForResource(IFile moduleFile, String resource) throws Exception { List<IFile> files = getFilesForResources(moduleFile, ImmutableList.of(resource)); return !files.isEmpty() ? files.get(0) : null; } /** * Returns the {@link IFile}'s for "public" resources in given module. An {@link IFile} for * resource is returned only if this resource located in some IProject in workspace, not just in * jar. * * @param moduleFile * the module *.gwt.xml file. * @param resources * the paths of "public" resources, relative to the one of the "public" package of some * module. */ public static List<IFile> getFilesForResources(IFile moduleFile, Collection<String> resources) throws Exception { // prepare IContainer's for each "src" folder List<IContainer> sourceFolders; { IJavaProject javaProject = JavaCore.create(moduleFile.getProject()); sourceFolders = CodeUtils.getSourceContainers(javaProject, true); } // fill list of IFile's List<IFile> files = Lists.newArrayList(); for (String resourcePath : resources) { IFile file = getFileForResource(moduleFile, sourceFolders, resourcePath); if (file != null) { files.add(file); } } // return IFile's return files; } private static IFile getFileForResource(IFile moduleFile, final List<IContainer> sourceFolders, final String resourcePath) throws Exception { final IFile files[] = new IFile[1]; // check public resources ModuleVisitor.accept(new DefaultModuleDescription(moduleFile), new ModuleVisitor() { @Override public void visitPublicPackage(ModuleElement module, String packageName) throws Exception { for (IContainer sourceContainer : sourceFolders) { checkContainer(packageName, sourceContainer); } } private void checkContainer(String packageName, IContainer sourceContainer) { if (files[0] == null) { String filePath = packageName.replace('.', '/') + "/" + resourcePath; IFile file = sourceContainer.getFile(new Path(filePath)); if (file.exists()) { files[0] = file; } } } }); // check "web" folder if (files[0] == null) { files[0] = getFileForResource_webFolder(moduleFile, resourcePath); } // done return files[0]; } /** * @return the existing resource in "web" folder, may be <code>null</code>. */ private static IFile getFileForResource_webFolder(IFile moduleFile, String pathInWebFolder) { IProject project = moduleFile.getProject(); String webFolderName = WebUtils.getWebFolderName(project); IFolder webFolder = project.getFolder(webFolderName); if (webFolder != null && webFolder.exists()) { IFile file = webFolder.getFile(new Path(pathInWebFolder)); if (file.exists()) { return file; } } return null; } /** * @return <code>true</code> if resource with given path exists in module. */ public static boolean isExistingResource(ModuleDescription moduleDescription, String path) throws Exception { InputStream stream = getResource(moduleDescription, path); IOUtils.closeQuietly(stream); return stream != null; } /** * 1. GWT 1.5 under 1.5. Resource path is path in "public", for example just "home.gif". * <p> * 2. GWT 1.5 under 1.6. In GWT 1.6 all resources should be in "web", including resources from * "public", which copied into sub-folder with "logical" module name (fully qualified name or * value of "rename-to"). So, to access resource from "web" itself, use just "home.gif". To access * resource from "my.long.module.Name" with "rename-to=module", use "module/home.gif". Note, that * copying "public" resources happens only when you run hosted mode, but not when you just use * design time, i.e. it should be considered as "virtual" copying. So, we should first check * "public" folders and if not found, check "web". * <p> * 3. GWT 1.6 under 1.6. */ public static InputStream getResource(ModuleDescription moduleDescription, String path) throws Exception { // try "public" module resource { InputStream result = getResource_modulePublic(moduleDescription, path); if (result != null) { return result; } } // check "web" folder IProject project = moduleDescription.getProject(); String webFolderName = WebUtils.getWebFolderName(project); IPath webFolderPath = new Path(webFolderName); IFolder webFolder = project.getFolder(webFolderPath); if (webFolder != null) { IFile file = webFolder.getFile(new Path(path)); if (file.exists()) { InputStream result = file.getContents(true); // If CSS, then return UTF-8 bytes. if (result != null && path.toUpperCase().endsWith(".CSS")) { String stringContent = new String(IOUtils2.readBytes(result), file.getCharset()); result = new ByteArrayInputStream(stringContent.getBytes("UTF-8")); } return result; } } // no resource return null; } /** * If given path has module name as prefix, then resource may be in "public" folder. */ private static InputStream getResource_modulePublic(ModuleDescription moduleDescription, String path) throws Exception { // prepare "public" path final String publicResourcePath; { ModuleElement module = readModule(moduleDescription); String prefix = module.getName() + "/"; if (path.startsWith(prefix)) { publicResourcePath = path.substring(prefix.length()); } else { publicResourcePath = path; } } // check "public" folders final InputStream[] result = new InputStream[1]; ModuleVisitor.accept(moduleDescription, new ModuleVisitor() { @Override public void visitPublicPackage(ModuleElement module, String packageName) throws Exception { if (result[0] == null) { String fullResourcePath = packageName.replace('.', '/') + "/" + publicResourcePath; result[0] = getResourcesProvider().getResourceAsStream(fullResourcePath); } } }); return result[0]; } /*private static InputStream getResource_modulePublic(IFile moduleFile, String path) throws Exception { ModuleElement module = readModule(moduleFile); String prefix = module.getName() + "/"; if (path.startsWith(prefix)) { final String publicResourcePath = path.substring(prefix.length()); final InputStream[] result = new InputStream[1]; ModuleVisitor.accept(moduleFile, new ModuleVisitor() { @Override public void visitPublicPackage(ModuleElement module, String packageName) throws Exception { if (result[0] == null) { String fullResourcePath = packageName.replace('.', '/') + "/" + publicResourcePath; result[0] = getResourcesProvider().getResourceAsStream(fullResourcePath); } } }); return result[0]; } // not module "public" path return null; }*/ /** * @return the default name of HTML file for given module name. */ public static String getDefaultHTMLName(String moduleId) { return StringUtils.substringAfterLast(moduleId, ".") + ".html"; } //////////////////////////////////////////////////////////////////////////// // // *.css/*.js resources // //////////////////////////////////////////////////////////////////////////// /** * @return HTML of the first '!doctype' tag (if any), otherwise returns empty string. Note: return * <code>null</code> if '!doctype' value couldn't be determined. */ public static String getDocType(ModuleDescription moduleFile) throws Exception { IFile htmlFile = getHTMLFile(moduleFile); if (htmlFile != null) { List<TagNode> tags = getHTMLFileTags(htmlFile, "!doctype"); for (TagNode tag : tags) { return tag.toHtml(); } return ""; } return null; } /** * Gets "public" paths of the CSS resources referenced by module. There are two sources for CSS * resources: * <ol> * <li><code>link</code> tags in HTML file;</li> * <li><code>stylesheet</code> tags in module file.</li> * </ol> * <p> * It expects that unit is inside of some GWT module with single module file. It also expects that * default HTML file is used, see {@link #getHTMLFile(IFile)}. */ public static List<String> getCssResources(ModuleDescription moduleDescription) throws Exception { final List<String> cssResources = Lists.newArrayList(); // add CSS resources from module file, *.gwt.xml ModuleVisitor.accept(moduleDescription, new ModuleVisitor() { @Override public void endVisitModule(ModuleElement module) { for (StylesheetElement stylesheetElement : module.getStylesheetElements()) { String src = stylesheetElement.getSrc(); if (src.startsWith("/")) { src = src.substring(1); } if (src.startsWith("../")) { src = src.substring(3); } cssResources.add(src); } } }); // add CSS resources from HTML { IFile htmlFile = getHTMLFile(moduleDescription); if (htmlFile != null) { String resourcePrefix = getHTMLResourcePrefix(htmlFile); List<TagNode> tags = getHTMLFileTags(htmlFile, "link"); for (TagNode tag : tags) { if ("stylesheet".equals(tag.getAttribute("rel"))) { String href = tag.getAttribute("href"); String resourcePath = resourcePrefix + href; cssResources.add(resourcePath); } } } } // OK, we have all CSS resources for module return cssResources; } /** * Resources referenced from HTML file are located relative to folder of HTML file. */ private static String getHTMLResourcePrefix(IFile htmlFile) throws CoreException { String htmlPath = (String) htmlFile.getSessionProperty(KEY_RESOURCE_PATH); int index = htmlPath.indexOf("/"); return index == -1 ? "" : htmlPath.substring(0, index + 1); } /** * Gets "public" paths of the CSS resources referenced by module. There are two sources for CSS * resources: * <ol> * <li><code>script</code> tags in HTML file;</li> * <li><code>script</code> tags in module file.</li> * </ol> * <p> * It expects that unit is inside of some GWT module with single module file. It also expects that * default HTML file is used, see {@link #getHTMLFile(IFile)}. */ public static List<String> getScriptResources(ModuleDescription moduleDescription) throws Exception { final List<String> scriptResources = Lists.newArrayList(); // add <script> resources from HTML { IFile htmlFile = getHTMLFile(moduleDescription); if (htmlFile != null) { List<TagNode> tags = getHTMLFileTags(htmlFile, "script"); for (TagNode tag : tags) { String src = tag.getAttribute("src"); if (src != null) { scriptResources.add(src); } } } } // add CSS resources from module file, *.gwt.xml ModuleVisitor.accept(moduleDescription, new ModuleVisitor() { @Override public void endVisitModule(ModuleElement module) { for (ScriptElement scriptElement : module.getScriptElements()) { String src = scriptElement.getSrc(); if (src != null) { //scriptResources.add(mainModuleName + "/" + src); scriptResources.add(src); } } } }); // exclude some scripts for (Iterator<String> I = scriptResources.iterator(); I.hasNext();) { String src = I.next(); // GWT 1.4+ system script if (src.indexOf(".nocache.js") != -1) { I.remove(); } // Google Maps if (src.indexOf("maps.google.com/maps?") != -1) { I.remove(); } } // OK, we have all CSS resources for module return scriptResources; } private static final QualifiedName KEY_RESOURCE_PATH = new QualifiedName("com.google.gdt.eclipse.designer", "resourcePath"); /** * @return the {@link IFile} for module HTML file, may be <code>null</code>. */ public static IFile getHTMLFile(ModuleDescription moduleDescription) throws Exception { // IFile can only be returned if this ModuleDescription is a DefaultModuleDescription, // aka, if it wraps an IFile itself IFile moduleFile; if (moduleDescription instanceof DefaultModuleDescription) { moduleFile = ((DefaultModuleDescription) moduleDescription).getFile(); } else { return null; } // try welcome-file { IFile file = getHTMLFile_web(moduleFile); if (file != null) { return file; } } // try default HTML String moduleId = moduleDescription.getId(); String htmlFileName = getDefaultHTMLName(moduleId); IFile file = getFileForResource(moduleFile, htmlFileName); if (file != null) { file.setSessionProperty(KEY_RESOURCE_PATH, htmlFileName); } return file; } /** * Tries to find HTML file specified in <code>web.xml</code>, as "welcome-file". * * @return the {@link IFile} for HTML file, may be <code>null</code>. */ private static IFile getHTMLFile_web(IFile moduleFile) throws Exception { IFile webFile = getFileForResource_webFolder(moduleFile, "WEB-INF/web.xml"); if (webFile == null) { return null; } // ignore if empty String webFileContent = IOUtils2.readString(webFile); if (StringUtils.isBlank(webFileContent)) { return null; } // search for "welcome-file" List<String> welcomeFileNames = Lists.newArrayList(); try { WebDocumentEditContext context = new WebDocumentEditContext(webFile); try { WebAppElement webApp = context.getWebAppElement(); for (WelcomeFileListElement welcomeFileList : webApp.getWelcomeFileListElements()) { for (WelcomeFileElement welcomeFile : welcomeFileList.getWelcomeFiles()) { String welcomeFileName = welcomeFile.getName(); welcomeFileNames.add(welcomeFileName); } } } finally { context.disconnect(); } } catch (Throwable e) { throw new DesignerException(IExceptionConstants.INVALID_WEB_XML, e, webFile.getFullPath().toPortableString(), webFileContent); } // try to find resource for "welcome-file" for (String welcomeFileName : welcomeFileNames) { IFile file = getFileForResource(moduleFile, welcomeFileName); if (file != null) { file.setSessionProperty(KEY_RESOURCE_PATH, welcomeFileName); return file; } } // not found return null; } /** * Uses "htmlParser" library to parse given HTML and extract tags with given name. * * @return the {@link List} of found {@link TagNode}'s. */ private static List<TagNode> getHTMLFileTags(IFile htmlFile, String tagName) throws Exception { // find nodes Node[] tags; { String htmlContents = IOUtils2.readString(htmlFile); Lexer lexer = new Lexer(new Page(htmlContents)); Parser parser = new Parser(lexer, new DefaultParserFeedback(DefaultParserFeedback.QUIET)); TagFindingVisitor visitor = new TagFindingVisitor(new String[] { tagName }); parser.visitAllNodesWith(visitor); tags = visitor.getTags(0); } // convert into List<TagNode> List<TagNode> tagNodes = Lists.newArrayList(); CollectionUtils.addAll(tagNodes, tags); return tagNodes; } //////////////////////////////////////////////////////////////////////////// // // Locale // //////////////////////////////////////////////////////////////////////////// /** * @return the default locale from "set-property-fallback" property in module file. */ public static String getDefaultLocale(ModuleDescription moduleDescription) throws Exception { final AtomicReference<String> defaultLocale = new AtomicReference<String>("default"); ModuleVisitor.accept(moduleDescription, new ModuleVisitor() { @Override public void endVisitModule(ModuleElement module) { List<SetPropertyFallbackElement> elements = module.getSetPropertyFallbackElements(); for (SetPropertyFallbackElement element : elements) { if (element.getName().equals("locale")) { defaultLocale.set(element.getValue()); } } } }); return defaultLocale.get(); } //////////////////////////////////////////////////////////////////////////// // // RemoteService interface/impl // //////////////////////////////////////////////////////////////////////////// /** * Checks that given resource is Java file with remote service. */ public static boolean isRemoteService(IResource resource) throws CoreException { if (resource != null && resource instanceof IFile && resource.getName().endsWith(".java")) { ICompilationUnit cu = (ICompilationUnit) JavaCore.create(resource); return isRemoteService(cu); } return false; } /** * Checks that given Java element is part of unit that has RemoteService. */ public static boolean isRemoteService(IJavaElement element) throws CoreException { IType type = CodeUtils.getType(element); if (type != null && type.exists() && type.isInterface()) { return CodeUtils.isSuccessorOf(type, Constants.CLASS_REMOTE_SERVICE); } return false; } /** * Checks that given Java element is part of unit that has RemoteService. */ public static boolean isRemoteServiceImpl(IJavaElement element) throws Exception { IType type = CodeUtils.getType(element); if (type != null && type.exists()) { return CodeUtils.isSuccessorOf(type, Constants.CLASS_REMOTE_SERVICE_IMPL); } return false; } //////////////////////////////////////////////////////////////////////////// // // EntryPoint // //////////////////////////////////////////////////////////////////////////// /** * Checks that given Java element is part part of unit that has EntryPoint. */ public static boolean isEntryPoint(IJavaElement element) throws Exception { IType type = CodeUtils.getType(element); if (type != null && type.exists()) { return CodeUtils.isSuccessorOf(type, Constants.CLASS_ENTRY_POINT); } return false; } //////////////////////////////////////////////////////////////////////////// // // Progress // //////////////////////////////////////////////////////////////////////////// public static final NullProgressMonitor NULL_PROGRESS_MONITOR = new NullProgressMonitor(); /** * @return the given {@link IProgressMonitor} is it is not <code>null</code> or shared * {@link NullProgressMonitor} instance. */ public static IProgressMonitor getNonNullMonitor(IProgressMonitor monitor) { if (monitor == null) { monitor = NULL_PROGRESS_MONITOR; } return monitor; } //////////////////////////////////////////////////////////////////////////// // // Projects // //////////////////////////////////////////////////////////////////////////// /** * @return the {@link IJavaProject} with given name. */ public static IJavaProject getJavaProject(String name) { IProject project = getProject(name); return JavaCore.create(project); } /** * @return the {@link IProject} with given name. */ public static IProject getProject(String name) { return ResourcesPlugin.getWorkspace().getRoot().getProject(name); } /** * @return <code>true</code> if given project is GWT project. */ public static boolean isGWTProject(final IJavaProject javaProject) { return ExecutionUtils.runObjectIgnore(new RunnableObjectEx<Boolean>() { public Boolean runObject() throws Exception { return ProjectUtils.hasType(javaProject, "com.google.gwt.user.client.ui.Widget"); } }, false); } /** * @return <code>true</code> if given project is GWT project. */ public static boolean isGWTProject(final IProject project) { return ExecutionUtils.runObjectIgnore(new RunnableObjectEx<Boolean>() { public Boolean runObject() throws Exception { IJavaProject javaProject = JavaCore.create(project); return isGWTProject(javaProject); } }, false); } /** * @return <code>true</code> if given project is GPE GWT project. */ public static boolean isGpeGwtProject(IProject project) { return ProjectUtils.hasNature(project, Constants.GPE_NATURE_ID) && isGWTProject(project); } /** * Convenience method to get access to the {@link IJavaModel}. */ public static IJavaModel getJavaModel() { IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); return JavaCore.create(workspaceRoot); } /** * @return the GWT Java projects. */ public static List<IJavaProject> getGWTProjects() throws CoreException { List<IJavaProject> gwtProjects = Lists.newArrayList(); for (IJavaProject javaProject : getJavaModel().getJavaProjects()) { if (isGWTProject(javaProject.getProject())) { gwtProjects.add(javaProject); } } return gwtProjects; } //////////////////////////////////////////////////////////////////////////// // // AST // //////////////////////////////////////////////////////////////////////////// /** * Parse given model {@link ICompilationUnit} into AST {@link CompilationUnit}. */ public static CompilationUnit parseUnit(ICompilationUnit compilationUnit) { ASTParser parser = ASTParser.newParser(AST.JLS3); parser.setSource(compilationUnit); parser.setResolveBindings(true); return (CompilationUnit) parser.createAST(null); } }