/******************************************************************************* * Copyright (c) 2011 Oracle Corporation. * 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 * * Contributors: * Andrew McCulloch - initial API and implementation *******************************************************************************/ package org.eclipse.jst.jsf.core.jsfappconfig; import java.util.LinkedHashSet; import java.util.Set; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IPath; import org.eclipse.jdt.core.ElementChangedEvent; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IElementChangedListener; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaElementDelta; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jst.jsf.common.internal.componentcore.AbstractVirtualComponentQuery.DefaultVirtualComponentQuery; import org.eclipse.jst.jsf.core.jsfappconfig.AbstractJSFAppConfigLocater; import org.eclipse.jst.jsf.core.jsfappconfig.JSFAppConfigUtils; import org.eclipse.wst.common.componentcore.resources.IVirtualFolder; /** * AnnotationJSFAppConfigLocator locates JSF configuration specified as JSF 2.x annotations. * * <p><b>Provisional API - subject to change</b></p> * * @author Andrew McCulloch - Oracle */ public class AnnotationJSFAppConfigLocator extends AbstractJSFAppConfigLocater { private final IElementChangedListener listener = new ElementChangeListener(); private IPath webInfLibPath = null; private IPath webInfClassesPath = null; public void startLocating() { IProject project = getJSFAppConfigManager().getProject(); if (JSFAppConfigUtils.isValidJSFProject(project, "2.0")) { //$NON-NLS-1$ IVirtualFolder webContent = new DefaultVirtualComponentQuery().getWebContentFolder(project); if (webContent != null) { IContainer webContentFolder = webContent.getUnderlyingFolder(); if (webContentFolder != null && webContentFolder.exists()) { IPath webContentPath = webContentFolder.getProjectRelativePath(); if (webContentPath != null) { webInfLibPath = webContentPath.append("WEB-INF/lib"); //$NON-NLS-1$ webInfClassesPath = webContentPath.append("WEB-INF/classes"); //$NON-NLS-1$ addProvider(); JavaCore.addElementChangedListener(listener); } } } } } private void addProvider() { Set newConfigProviders = new LinkedHashSet(); newConfigProviders.add(new AnnotationJSFAppConfigProvider()); updateConfigProviders(newConfigProviders); } @Override public void stopLocating() { JavaCore.removeElementChangedListener(listener); } private class ElementChangeListener implements IElementChangedListener { public void elementChanged(ElementChangedEvent event) { if (isRelevantChange(event.getDelta(), getJSFAppConfigManager().getProject())) { addProvider(); } } /* * 11.5.1 Requirements for scanning of classes for annotations = * [P1_start-annotation-discovery]If the <faces-config> element in the * WEB-INF/faces-config.xml file contains metadata-complete attribute * whose value is "true", the implementation must not perform annotation * scanning on any classes except for those classes provided by the * implementation itself. Otherwise, continue as follows. = If the * runtime discovers a conflict between an entry in the Application * Configuration Resources and an annotation, the entry in the * Application Configuration Resources takes precedence. = All classes * in WEB-INF/classes must be scanned. = For every jar in the * application's WEB-INF/lib directory, if the jar contains a * "META-INF/faces-config.xml" file or a file that matches the regular * expression ".*\.faces-config.xml" (even an empty one), all classes in * that jar must be scanned.[P1_end-annotation-discovery] */ private final boolean isRelevantChange(IJavaElementDelta delta, IProject project) { int deltaFlags = delta.getFlags(); /* * F_CONTENT means the content of an element changed. If the element is a class in web-inf/classes this is relevant * F_ADDED_TO_CLASSPATH, F_ARCHIVE_CONTENT_CHANGED, F_REMOVED_FROM_CLASSPATH all indicate archive changes which are relevant * if the archive has the metadata and is in web-inf/lib. * F_CLASSPATH_REORDER could indicate a class with a bean annotation has been discovered or hidden by classpath changes. */ if ((deltaFlags & (IJavaElementDelta.F_CONTENT | IJavaElementDelta.F_ADDED_TO_CLASSPATH | IJavaElementDelta.F_ARCHIVE_CONTENT_CHANGED | IJavaElementDelta.F_REMOVED_FROM_CLASSPATH | IJavaElementDelta.F_REORDER | IJavaElementDelta.F_PRIMARY_RESOURCE)) != 0) { IJavaElement changedElement = delta.getElement(); switch (changedElement.getElementType()) { case IJavaElement.COMPILATION_UNIT: if (changedElement instanceof ICompilationUnit) { return true; } IType type = (IType)changedElement; IPath classFilePath = type.getPath(); if (classFilePath != null) { if (project.getFullPath().append(webInfClassesPath).isPrefixOf(classFilePath)) { return true; } } case IJavaElement.PACKAGE_FRAGMENT_ROOT: IPackageFragmentRoot root = (IPackageFragmentRoot)changedElement; if (root.isArchive() && !root.isExternal()) { IPath archivePath = root.getPath(); if (archivePath != null) { if (project.getFullPath().append(webInfLibPath).isPrefixOf(archivePath)) { if ((deltaFlags & IJavaElementDelta.F_REMOVED_FROM_CLASSPATH) != 0) { return true; } //don't bother processing this delta or children if the root is missing AnnotationPackageFragmentRoot wrapper = new AnnotationPackageFragmentRoot(root); return wrapper.canContainAnnotatedComponents(); } } } default://do nothing } } IJavaElementDelta[] childDeltas = delta.getAffectedChildren(); if (childDeltas != null) { for (IJavaElementDelta childDelta : childDeltas) { if (isRelevantChange(childDelta, project)) { return true; } } } return false; } } }