/******************************************************************************* * Copyright (c) 2011 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.jsf.jsf2.bean.build; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; 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.IResourceDelta; import org.eclipse.core.resources.IResourceDeltaVisitor; import org.eclipse.core.resources.IResourceVisitor; import org.eclipse.core.resources.IncrementalProjectBuilder; 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.OperationCanceledException; import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageDeclaration; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.internal.core.JavaModelManager; import org.jboss.tools.common.EclipseUtil; import org.jboss.tools.common.model.XModelObject; import org.jboss.tools.common.model.plugin.ModelPlugin; import org.jboss.tools.common.model.util.EclipseResourceUtil; import org.jboss.tools.common.web.WebUtils; import org.jboss.tools.jsf.JSFModelPlugin; import org.jboss.tools.jsf.jsf2.bean.model.JSF2ProjectFactory; import org.jboss.tools.jsf.jsf2.bean.model.impl.DefinitionContext; import org.jboss.tools.jsf.jsf2.bean.model.impl.FacesConfigDefinition; import org.jboss.tools.jsf.jsf2.bean.model.impl.JSF2Project; import org.jboss.tools.jsf.jsf2.bean.model.impl.TypeDefinition; import org.jboss.tools.jsf.jsf2.bean.scanner.FileSet; import org.jboss.tools.jst.web.kb.internal.IIncrementalProjectBuilderExtension; import org.jboss.tools.jst.web.kb.internal.KbBuilder; public class JSF2ProjectBuilder extends IncrementalProjectBuilder implements IIncrementalProjectBuilderExtension { JSF2ResourceVisitor resourceVisitor = null; /** * Set only for instance created to initially load jsf2 model. */ JSF2Project jsf; public JSF2ProjectBuilder() {} public JSF2ProjectBuilder(JSF2Project jsf) throws CoreException { this.jsf = jsf; build(IncrementalProjectBuilder.FULL_BUILD, null, new NullProgressMonitor()); } protected JSF2Project getJSF2Project() { if(jsf != null) { return jsf; } IProject p = getProject(); if(p == null) return null; return (JSF2Project)JSF2ProjectFactory.getJSF2Project(p, false); } IProject getCurrentProject() { return jsf != null ? jsf.getProject() : getProject(); } JSF2ResourceVisitor getResourceVisitor() { if(resourceVisitor == null) { resourceVisitor = new JSF2ResourceVisitor(); } return resourceVisitor; } @Override public IProject[] build(int kind, Map<String, String> args, IProgressMonitor monitor) throws CoreException { resourceVisitor = null; JSF2Project n = getJSF2Project(); if(n == null) { return null; } if(n.hasNoStorage()) { kind = FULL_BUILD; } n.postponeFiring(); //Set true when canceling build should be recovered by complete rebuild pf classpath at next build. boolean classPathShouldBeReset = false; try { n.resolveStorage(kind != FULL_BUILD); if(kind == FULL_BUILD) n.getClassPath().reset(); //1. Check class path. boolean isClassPathUpdated = n.getClassPath().update(); Map<String, XModelObject> newJars = new HashMap<String, XModelObject>(); if(isClassPathUpdated || kind == FULL_BUILD) { classPathShouldBeReset = true; //2. Update class path. Removed paths will be cached to be applied to working copy of context. n.getClassPath().setSrcs(getResourceVisitor().srcs); newJars = n.getClassPath().process(); }int i = 0; //4. Create working copy of context. n.getDefinitions().newWorkingCopy(kind == FULL_BUILD); //5. Modify working copy of context. //5.1 Apply Removed paths. if(isClassPathUpdated) { n.getClassPath().applyRemovedPaths(); } //5.2 Discover sources and build definitions. if(isClassPathUpdated) { buildJars(newJars, monitor); n.getClassPath().validateProjectDependencies(); kind = FULL_BUILD; } else if(n.getClassPath().hasToUpdateProjectDependencies()) { n.getClassPath().validateProjectDependencies(); } if (kind == FULL_BUILD) { fullBuild(monitor); } else { IResourceDelta delta = getDelta(getCurrentProject()); if (delta == null) { fullBuild(monitor); } else { incrementalBuild(delta, monitor); } } // 6. Save created definitions to project context and build beans. getJSF2Project().getDefinitions().applyWorkingCopy(); try { n.store(); } catch (IOException e) { JSFModelPlugin.getDefault().logError(e); //$NON-NLS-1$ } } catch (OperationCanceledException e) { //Recover partially built model. if(classPathShouldBeReset) { getJSF2Project().getClassPath().clean(); } getJSF2Project().getDefinitions().dropWorkingCopy(); throw e; } finally { n.fireChanges(); } return null; } protected void fullBuild(final IProgressMonitor monitor) throws CoreException { try { JSF2ResourceVisitor rv = getResourceVisitor(); rv.setProgressMonitor(monitor); rv.incremental = false; getCurrentProject().accept(rv); FileSet fs = rv.fileSet; build(fs, getJSF2Project(), monitor); } catch (CoreException e) { JSFModelPlugin.getDefault().logError(e); } } protected void incrementalBuild(IResourceDelta delta, IProgressMonitor monitor) throws CoreException { JSF2ResourceVisitor rv = getResourceVisitor(); rv.setProgressMonitor(monitor); rv.incremental = true; delta.accept(new SampleDeltaVisitor()); FileSet fs = rv.fileSet; build(fs, getJSF2Project(), monitor); } protected void buildJars(Map<String, XModelObject> newJars, IProgressMonitor monitor) throws CoreException { IJavaProject jp = EclipseResourceUtil.getJavaProject(getJSF2Project().getProject()); if(jp == null) return; JavaModelManager manager = JavaModelManager.getJavaModelManager(); try { manager.cacheZipFiles(this); FileSet fileSet = new FileSet(); for (String jar: newJars.keySet()) { Path path = new Path(jar); IPackageFragmentRoot root = jp.getPackageFragmentRoot(jar); if(root == null) continue; if(!root.exists()) { IFile f = EclipseResourceUtil.getFile(jar); if(f != null && f.exists()) { root = jp.getPackageFragmentRoot(f); } else { f = EclipseResourceUtil.getFile(jar + "/META-INF/web-fragment.xml"); if(f != null && f.exists()) { root = jp.getPackageFragmentRoot(f.getParent().getParent()); } } } if (root == null || !root.exists()) continue; IJavaElement[] es = root.getChildren(); for (IJavaElement e : es) { if (e instanceof IPackageFragment) { IPackageFragment pf = (IPackageFragment) e; IClassFile[] cs = pf.getClassFiles(); for (IClassFile c : cs) { KbBuilder.checkCanceled(monitor); fileSet.add(path, c.getType()); } } } } build(fileSet, getJSF2Project(), monitor); } finally { manager.flushZipFiles(this); } } void build(FileSet fs, JSF2Project project, IProgressMonitor monitor) { DefinitionContext context = getJSF2Project().getDefinitions().getWorkingCopy(); Map<IPath, Set<IType>> cs = fs.getClasses(); for (IPath f: cs.keySet()) { Set<IType> ts = cs.get(f); for (IType type: ts) { KbBuilder.checkCanceled(monitor); TypeDefinition def = new TypeDefinition(); def.setType(type, context, TypeDefinition.FLAG_ALL_MEMBERS); context.addType(f, type.getFullyQualifiedName(), def); } } IFile facesConfig = fs.getFacesConfig(); if(facesConfig != null) { FacesConfigDefinition def = new FacesConfigDefinition(); def.setPath(facesConfig.getFullPath()); XModelObject o = EclipseResourceUtil.createObjectForResource(facesConfig); if(o != null) { def.setObject(o); context.setFacesConfig(def); } } } class SampleDeltaVisitor implements IResourceDeltaVisitor { /* * @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta) */ public boolean visit(IResourceDelta delta) throws CoreException { IResource resource = delta.getResource(); switch (delta.getKind()) { case IResourceDelta.ADDED: return getResourceVisitor().visit(resource); case IResourceDelta.REMOVED: JSF2Project p = getJSF2Project(); if(p != null) { p.getDefinitions().getWorkingCopy().clean(resource.getFullPath()); } break; case IResourceDelta.CHANGED: return getResourceVisitor().visit(resource); } //return true to continue visiting children. return true; } } class JSF2ResourceVisitor implements IResourceVisitor { boolean incremental = false; FileSet fileSet = new FileSet(); IPath[] outs = new IPath[0]; IPath[] srcs = new IPath[0]; IPath[] webinfs = new IPath[0]; Set<IPath> visited = new HashSet<IPath>(); IProgressMonitor monitor = null; public JSF2ResourceVisitor() { getJavaSourceRoots(getCurrentProject()); webinfs = WebUtils.getWebInfPaths(getCurrentProject()); } public void setProgressMonitor(IProgressMonitor monitor) { this.monitor = monitor; } void getJavaSourceRoots(IProject project) { IJavaProject javaProject = EclipseResourceUtil.getJavaProject(project); if(javaProject == null) return; List<IPath> ps = new ArrayList<IPath>(); List<IPath> os = new ArrayList<IPath>(); try { IPath output = javaProject.getOutputLocation(); if(output != null) os.add(output); IClasspathEntry[] es = javaProject.getResolvedClasspath(true); for (int i = 0; i < es.length; i++) { if(es[i].getEntryKind() == IClasspathEntry.CPE_SOURCE) { IResource findMember = ModelPlugin.getWorkspace().getRoot().findMember(es[i].getPath()); if(findMember != null && findMember.exists()) { ps.add(findMember.getFullPath()); } IPath out = es[i].getOutputLocation(); if(out != null && !os.contains(out)) { os.add(out); } } } srcs = ps.toArray(new IPath[0]); outs = os.toArray(new IPath[0]); } catch(CoreException ce) { JSFModelPlugin.getDefault().logError("Error while locating java source roots for " + project, ce); } } @Override public boolean visit(IResource resource) throws CoreException { KbBuilder.checkCanceled(monitor); IPath path = resource.getFullPath(); if(resource instanceof IFile) { if(visited.contains(path)) { return false; } visited.add(path); IFile f = (IFile)resource; for (int i = 0; i < outs.length; i++) { if(outs[i].isPrefixOf(path)) { return false; } } for (int i = 0; i < srcs.length; i++) { if(srcs[i].isPrefixOf(path)) { if(f.getName().endsWith(".java")) { ICompilationUnit unit = EclipseUtil.getCompilationUnit(f); if(unit!=null) { if(f.getName().equals("package-info.java")) { IPackageDeclaration[] pkg = unit.getPackageDeclarations(); if(pkg != null && pkg.length > 0) { fileSet.add(f.getFullPath(), pkg[0]); if(incremental) { IResource[] ms = resource.getParent().members(); for (IResource m: ms) { if(m instanceof IFile && !m.getName().equals("package-info.java")) { visit(m); } } } } } else { IType[] ts = unit.getTypes(); fileSet.add(f.getFullPath(), ts); } } } Set<IFile> ds = getDependentFiles(path, visited); if(ds != null) for (IFile d: ds) visit(d); return false; } } for (int i = 0; i < webinfs.length; i++) { if(webinfs[i].isPrefixOf(path) && f.getName().equals("faces-config.xml") && path.removeLastSegments(1).equals(webinfs[i])) { fileSet.setFacesConfig(f); } } Set<IFile> ds = getDependentFiles(path, visited); if(ds != null) for (IFile d: ds) visit(d); } if(resource instanceof IFolder) { for (int i = 0; i < outs.length; i++) { if(outs[i].isPrefixOf(path)) { return false; } } for (int i = 0; i < srcs.length; i++) { if(srcs[i].isPrefixOf(path) || path.isPrefixOf(srcs[i])) { return true; } } if(resource == resource.getProject()) { return true; } for (IPath webinf: webinfs) { if(webinf.isPrefixOf(path) || path.isPrefixOf(webinf)) { return true; } } return false; } //return true to continue visiting children. return true; } } //In case if we will need that Set<IFile> getDependentFiles(IPath path, Set<IPath> visited) { return null; } }