[291251] API needed to determine if project has no single root and reasons why
diff --git a/plugins/org.eclipse.jst.j2ee/j2eecreation/org/eclipse/jst/j2ee/internal/project/SingleRootStatus.java b/plugins/org.eclipse.jst.j2ee/j2eecreation/org/eclipse/jst/j2ee/internal/project/SingleRootStatus.java
new file mode 100644
index 0000000..52af73c
--- /dev/null
+++ b/plugins/org.eclipse.jst.j2ee/j2eecreation/org/eclipse/jst/j2ee/internal/project/SingleRootStatus.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2009 IBM Corporation and others.
+ * 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:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jst.j2ee.internal.project;
+
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jst.j2ee.internal.plugin.J2EEPlugin;
+import org.eclipse.jst.j2ee.project.ISingleRootStatus;
+import org.eclipse.wst.common.componentcore.internal.ComponentResource;
+
+public class SingleRootStatus extends Status implements ISingleRootStatus {
+
+	ComponentResource resource;
+
+	public SingleRootStatus(int type, int code, ComponentResource resource, String message, Throwable exception) {
+		super(type, J2EEPlugin.PLUGIN_ID, code, message, exception);
+		this.resource = resource;
+	}
+	
+	public SingleRootStatus(int code, ComponentResource resource, String message) {
+		this(getSeverity(code), code, resource, message, null);
+	}
+	
+	public ComponentResource getComponentResource() {
+		return resource;
+	}
+
+	protected static int getSeverity(int code) {
+		return code == 0 ? 0 : 1 << (code / 33);
+	}
+
+}
diff --git a/plugins/org.eclipse.jst.j2ee/j2eecreation/org/eclipse/jst/j2ee/project/ISingleRootStatus.java b/plugins/org.eclipse.jst.j2ee/j2eecreation/org/eclipse/jst/j2ee/project/ISingleRootStatus.java
new file mode 100644
index 0000000..912b905
--- /dev/null
+++ b/plugins/org.eclipse.jst.j2ee/j2eecreation/org/eclipse/jst/j2ee/project/ISingleRootStatus.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2009 IBM Corporation and others.
+ * 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:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jst.j2ee.project;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.wst.common.componentcore.internal.ComponentResource;
+
+public interface ISingleRootStatus extends IStatus {
+	
+	// Information Only [0-32]
+	
+	// Warnings [33-65]
+	public static final int SOURCE_PATH_NOT_FOUND = 33;
+	
+	// Errors [66-98]
+	public static final int NO_COMPONENT_FOUND = 67;
+
+	public static final int JAVA_OUTPUT_GREATER_THAN_1 = 68;
+
+	public static final int EAR_PROJECT_FOUND = 69;
+
+	public static final int LINKED_RESOURCES_FOUND = 70;
+
+	public static final int JAVA_OUTPUT_NOT_A_CONTENT_ROOT = 71;
+
+	public static final int JAVA_OUTPUT_NOT_WEBINF_CLASSES = 72;
+
+	public static final int ATLEAST_1_RESOURCE_MAP_MISSING = 73;
+
+	public static final int NO_RESOURCE_MAPS_FOUND = 74;
+
+	public static final int ONE_CONTENT_ROOT_REQUIRED = 75;
+
+	public static final int ATLEAST_1_JAVA_SOURCE_REQUIRED = 76;
+
+	public static final int RUNTIME_PATH_NOT_ROOT = 77;
+
+	public static final int SOURCE_NOT_JAVA_CONTAINER = 78;
+
+	public static final int RUNTIME_PATH_NOT_ROOT_OR_WEBINF_CLASSES = 79;
+
+	public static final int ONLY_1_CONTENT_ROOT_ALLOWED = 80;
+	
+	public ComponentResource getComponentResource();
+
+
+}
diff --git a/plugins/org.eclipse.jst.j2ee/j2eecreation/org/eclipse/jst/j2ee/project/SingleRootUtil.java b/plugins/org.eclipse.jst.j2ee/j2eecreation/org/eclipse/jst/j2ee/project/SingleRootUtil.java
new file mode 100644
index 0000000..db6eadd
--- /dev/null
+++ b/plugins/org.eclipse.jst.j2ee/j2eecreation/org/eclipse/jst/j2ee/project/SingleRootUtil.java
@@ -0,0 +1,484 @@
+/*******************************************************************************
+ * Copyright (c) 2009 IBM Corporation and others.
+ * 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:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jst.j2ee.project;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.resources.IContainer;
+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.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jst.j2ee.internal.J2EEConstants;
+import org.eclipse.jst.j2ee.internal.plugin.J2EEPlugin;
+import org.eclipse.jst.j2ee.internal.project.SingleRootStatus;
+import org.eclipse.jst.j2ee.internal.project.J2EEProjectUtilities;
+import org.eclipse.wst.common.componentcore.internal.ComponentResource;
+import org.eclipse.wst.common.componentcore.internal.Property;
+import org.eclipse.wst.common.componentcore.internal.StructureEdit;
+import org.eclipse.wst.common.componentcore.internal.WorkbenchComponent;
+import org.eclipse.wst.common.componentcore.resources.IVirtualComponent;
+
+public class SingleRootUtil {
+	
+	/** 
+	 * Used to return immediately after the first error code is found.
+	 */
+	public static final int INCLUDE_FIRST_ERROR = 0x08;
+	/** 
+	 * Used to capture all status codes (error, info, warning).
+	 */
+	public static final int INCLUDE_ALL = 0x07;
+	/** 
+	 * Used to capture all error and warning status codes.
+	 */
+	public static final int INCLUDE_ERRORS_AND_WARNINGS = 0x06;
+	/** 
+	 * Used to capture all error status codes.
+	 */
+	public static final int INCLUDE_ERRORS = 0x04;
+	
+	private static final int WARNINGS = 0x02;
+	private static final int INFO = 0x01;
+	private static final int NONE = 0x0;
+	private static String USE_SINGLE_ROOT_PROPERTY = "useSingleRoot"; //$NON-NLS-1$
+	private IVirtualComponent component;
+	private IPackageFragmentRoot[] cachedSourceContainers;
+	private IContainer[] cachedOutputContainers;
+	private boolean isSingleJavaOutputNonSource;
+	private MultiStatus wrapperStatus;
+	private int INCLUDE_FLAG;
+	
+	public SingleRootUtil(IVirtualComponent component) {
+		this.component = component;
+	}
+	
+	/**
+	 * Returns true if this module has a simple structure based on a
+	 * single root folder, and false otherwise.
+     *
+	 * In a single root structure, all files that are contained within the root folder
+	 * are part of the module, and are already in the correct module structure. No
+	 * module resources exist outside of this single folder.
+	 * 
+	 * @return true if this module has a single root structure, and
+	 *         false otherwise
+	 */
+	public boolean isSingleRoot() {
+		return validateSingleRoot(INCLUDE_FIRST_ERROR).getSeverity() != IStatus.ERROR;
+	}
+
+	/**
+	 *  Validates whether the component module has a single root structure.
+	 *  An IStatus with a severity of OK is returned for a valid single root 
+	 *  structure.  A MultiStatus containing children of type ISingleRootStatus
+	 *  is returned if any status codes were captured during the validation.
+	 *  A MultiStatus with a severity of INFO or WARNING is returned for a valid 
+	 *  single root structure containing status codes with no severities of ERROR.
+	 *  A MultiStatus with a severity of ERROR means the component does not have a
+	 *  valid single root structure.
+	 *  
+	 * @param flag - indicates the status codes (by severity) to capture 
+	 *               during the validation.  
+	 *               Valid flags are: INCLUDE_FIRST_ERROR
+	 *               				  INCLUDE_ALL
+	 *                                INCLUDE_ERRORS_AND_WARNINGS
+	 *                                INCLUDE_ERRORS
+	 *               
+	 * @return IStatus
+	 */
+	public IStatus validateSingleRoot(int flag) {
+		INCLUDE_FLAG = flag;
+		isSingleJavaOutputNonSource = false;
+		wrapperStatus = null;
+		StructureEdit edit = null;
+		try {
+			edit = StructureEdit.getStructureEditForRead(getProject());
+			if (edit == null || edit.getComponent() == null) {
+				reportStatus(ISingleRootStatus.NO_COMPONENT_FOUND);
+				return getStatus();
+			}
+		
+			WorkbenchComponent wbComp = edit.getComponent();
+			List resourceMaps = wbComp.getResources();
+			
+			// 229650 - check to see if the property 'useSingleRoot' is defined. If it is set and
+			// the value of the property is true then it will override the logic checks below
+			final List componentProperties = wbComp.getProperties();
+			if (componentProperties != null) {
+				final Iterator componentPropertiesIterator = componentProperties.iterator();
+				while (componentPropertiesIterator.hasNext()) {
+					Property wbProperty = (Property) componentPropertiesIterator.next();
+					if (USE_SINGLE_ROOT_PROPERTY.equals(wbProperty.getName())) {
+						boolean useSingleRoot = Boolean.valueOf(wbProperty.getValue()).booleanValue();
+						if (useSingleRoot) {
+							return Status.OK_STATUS;
+						}
+					}
+				}
+			}
+			
+			if (JavaEEProjectUtilities.isEARProject(getProject())) {
+				// Always return false for EARs so that members for EAR are always calculated and j2ee modules are filtered out
+				reportStatus(ISingleRootStatus.EAR_PROJECT_FOUND);
+			} else if (JavaEEProjectUtilities.isDynamicWebProject(getProject())) {
+				//validate web projects for single root
+				validateWebProject(resourceMaps);
+			} 
+			else if (JavaEEProjectUtilities.isEJBProject(getProject()) || JavaEEProjectUtilities.isJCAProject(getProject())
+					|| JavaEEProjectUtilities.isApplicationClientProject(getProject()) || JavaEEProjectUtilities.isUtilityProject(getProject())) {
+				
+				validateProject(resourceMaps);
+			}
+			//return the current status
+			return getStatus();
+		} finally {
+			cachedOutputContainers = null;
+			cachedSourceContainers = null;
+			if (edit != null)
+				edit.dispose();
+		}
+	}
+
+	private void validateProject(List resourceMaps) {
+		// if there are any linked resources then this is not a singleroot module
+		if (this.rootFoldersHaveLinkedContent()) {
+			reportStatus(ISingleRootStatus.LINKED_RESOURCES_FOUND);
+			if (INCLUDE_FLAG == NONE) return;
+		}
+		
+		// Ensure there are only source folder component resource mappings to the root content folder
+		if (isRootResourceMapping(resourceMaps)) {
+			IContainer[] javaOutputFolders = getJavaOutputFolders();
+			// Verify only one java outputfolder
+			if (javaOutputFolders.length==1) {
+				// By the time we get here we know: for any folders defined as source in the 
+				// .component file that they are also java source folders.
+				if (JavaEEProjectUtilities.isUtilityProject(getProject()) || 
+						JavaEEProjectUtilities.isEJBProject(getProject()) || 
+						JavaEEProjectUtilities.isApplicationClientProject(getProject())) {
+					
+					if (!isSourceContainer(javaOutputFolders[0].getFullPath())) {
+						// The single output folder is NOT a source folder so this is single-rooted. Since the
+						// output folder (something like classes or bin) is not a source folder, JDT copies all files
+						// (including non Java files) to this folder, so every resource needed at runtime is located 
+						// in a single directory.
+						isSingleJavaOutputNonSource  = true;
+						return;
+					} 
+				}
+				// Verify the java output folder is the same as one of the content roots
+				IPath javaOutputPath = getJavaOutputFolders()[0].getProjectRelativePath();
+				IContainer[] rootFolders = component.getRootFolder().getUnderlyingFolders();
+				for (int i=0; i<rootFolders.length; i++) {
+					IPath compRootPath = rootFolders[i].getProjectRelativePath();
+					if (javaOutputPath.equals(compRootPath))
+						return;
+				}
+				reportStatus(ISingleRootStatus.JAVA_OUTPUT_NOT_A_CONTENT_ROOT);
+			}
+			else {
+				reportStatus(ISingleRootStatus.JAVA_OUTPUT_GREATER_THAN_1);
+			}
+		}
+	}
+
+	private void validateWebProject(List resourceMaps) {
+		// if there are any linked resources then this is not a singleroot module
+		if (this.rootFoldersHaveLinkedContent()) {
+			reportStatus(ISingleRootStatus.LINKED_RESOURCES_FOUND);
+			if (INCLUDE_FLAG == NONE) return;
+		}
+		
+		// Ensure there are only basic component resource mappings -- one for the content folder 
+		// and any for src folders mapped to WEB-INF/classes
+		if (hasDefaultWebResourceMappings(resourceMaps)) {
+			// Verify only one java output folder
+			if (getJavaOutputFolders().length==1) {
+				// Verify the java output folder is to <content root>/WEB-INF/classes
+				IPath javaOutputPath = getJavaOutputFolders()[0].getProjectRelativePath();
+				IPath compRootPath = component.getRootFolder().getUnderlyingFolder().getProjectRelativePath();
+				if (compRootPath.append(J2EEConstants.WEB_INF_CLASSES).equals(javaOutputPath)) {
+					return;
+				}
+				else {
+					reportStatus(ISingleRootStatus.JAVA_OUTPUT_NOT_WEBINF_CLASSES);
+				}
+			}
+			else {
+				reportStatus(ISingleRootStatus.JAVA_OUTPUT_GREATER_THAN_1);
+			}
+		}
+	}
+
+	/**
+	 * Returns the root folders containing Java output in this module.
+	 * 
+	 * @return a possibly-empty array of Java output folders
+	 */
+	public IContainer[] getJavaOutputFolders() {
+		if (cachedOutputContainers == null)
+			cachedOutputContainers = getJavaOutputFolders(getProject());
+		return cachedOutputContainers;
+	}
+	
+	public IContainer[] getJavaOutputFolders(IProject project) {
+		if (project == null)
+			return new IContainer[0];
+		return J2EEProjectUtilities.getOutputContainers(project);
+	}
+	
+	/**
+	 * Checks if the path argument is to a source container for the project.
+	 * 
+	 * @param a workspace relative full path
+	 * @return is path a source container?
+	 */
+	private boolean isSourceContainer(IPath path) {
+		IPackageFragmentRoot[] srcContainers = getSourceContainers();
+		for (int i = 0; i < srcContainers.length; i++) {
+			if (srcContainers[i].getPath().equals(path))
+				return true;
+		}
+		return false;
+	}
+	
+	private IPackageFragmentRoot[] getSourceContainers() {
+		if (cachedSourceContainers == null)
+			cachedSourceContainers = J2EEProjectUtilities.getSourceContainers(getProject());
+		return cachedSourceContainers;
+	}
+	
+	/*
+     * This method returns true if the root folders of this component have any linked resources (folder or file);
+     * Otherwise false is returned.
+     */
+    private boolean rootFoldersHaveLinkedContent() {
+    	if (this.component != null) {
+    		final IContainer[] rootFolders = this.component.getRootFolder().getUnderlyingFolders();
+    		for (int i = 0; i < rootFolders.length; i++) {
+    			try {
+    				boolean hasLinkedContent = this.hasLinkedContent(rootFolders[i]);
+    				if (hasLinkedContent) {
+    					return true;
+    				}
+    			}
+    			catch (CoreException coreEx) {
+    				J2EEPlugin.logError(coreEx);
+    			}
+    		}
+    	}
+    	return false;
+    }
+    
+    /*
+     * If the resource to check is a file then this method will return true if the file is linked. If the resource to
+     * check is a folder then this method will return true if it, any of its sub directories, or any file contained
+     * with-in this directory of any of it's sub directories are linked. Otherwise false is returned.
+     */
+    private boolean hasLinkedContent(final IResource resourceToCheck) throws CoreException {
+    	if ((resourceToCheck != null) && resourceToCheck.isAccessible()) {
+    		// skip non-accessible files
+    		if (resourceToCheck.isLinked()) {
+    			return true;
+    		}
+    		else {
+    			switch (resourceToCheck.getType()) {
+    				case IResource.FOLDER:
+    					// recursively check sub directory contents
+    					final IResource[] subDirContents = ((IFolder) resourceToCheck).members();
+    					for (int i = 0; i < subDirContents.length; i++) {
+    						if (hasLinkedContent(subDirContents[i])) {
+    							return true;
+    						}
+    					}
+    					break;
+    				case IResource.FILE:
+    					return resourceToCheck.isLinked();
+    				default:
+    					// skip as we only care about files and folders
+    					break;
+    			}
+    		}
+    	}
+    	return false;
+    }
+    
+    /**
+	 * Ensure that any component resource mappings are for source folders and 
+	 * that they map to the root content folder
+	 * 
+	 * @param resourceMaps
+	 * @return boolean
+	 */
+	private boolean isRootResourceMapping(List resourceMaps) {
+		// If the list is empty, return false
+		if (resourceMaps.size() < 1) {
+			reportStatus(ISingleRootStatus.NO_RESOURCE_MAPS_FOUND);
+			return false;
+		}
+		
+		for (int i=0; i < resourceMaps.size(); i++) {
+			ComponentResource resourceMap = (ComponentResource) resourceMaps.get(i);
+			// Verify it maps to "/" for the content root
+			if (!resourceMap.getRuntimePath().equals(Path.ROOT)) {
+				reportStatus(ISingleRootStatus.RUNTIME_PATH_NOT_ROOT, resourceMap);
+				if (INCLUDE_FLAG == NONE) return false;
+			}
+			
+			// verify it is also a src container
+			IResource sourceResource = getProject().findMember(resourceMap.getSourcePath());
+			if (sourceResource != null && sourceResource.exists()){
+				IPath sourcePath = getProject().getFullPath().append(resourceMap.getSourcePath());
+				if (!isSourceContainer(sourcePath))
+					reportStatus(ISingleRootStatus.SOURCE_NOT_JAVA_CONTAINER, resourceMap);
+			}
+			else {
+				reportStatus(ISingleRootStatus.SOURCE_PATH_NOT_FOUND, resourceMap);
+			}
+			if (INCLUDE_FLAG == NONE) return false;
+		}
+		return true;
+	}
+	
+	/**
+	 * Ensure the default web setup is correct with one resource map and any number of java 
+	 * resource maps to WEB-INF/classes
+	 * 
+	 * @param resourceMaps
+	 * @return boolean
+	 */
+	private boolean hasDefaultWebResourceMappings(List resourceMaps) {
+		int rootValidMaps = 0;
+		int javaValidRoots = 0;
+		
+		// If there aren't at least 2 maps, return false
+		if (INCLUDE_FLAG == INCLUDE_FIRST_ERROR && resourceMaps.size() < 2) {
+			reportStatus(ISingleRootStatus.ATLEAST_1_RESOURCE_MAP_MISSING);
+			return false;
+		}
+		
+		IPath webInfClasses = new Path(J2EEConstants.WEB_INF_CLASSES).makeAbsolute();
+		for (int i = 0; i < resourceMaps.size(); i++) {
+			ComponentResource resourceMap = (ComponentResource) resourceMaps.get(i);
+			IPath sourcePath = getProject().getFullPath().append(resourceMap.getSourcePath());
+
+			// Verify if the map is for the content root
+			if (resourceMap.getRuntimePath().equals(Path.ROOT)) {
+				rootValidMaps++;
+			} 
+			// Verify if the map is for a java src folder and is mapped to "WEB-INF/classes"
+			else if (resourceMap.getRuntimePath().equals(webInfClasses)) {
+				if (exists(sourcePath)) {
+					if (isSourceContainer(sourcePath)) {
+						javaValidRoots++;
+					}
+					else {
+						reportStatus(ISingleRootStatus.SOURCE_NOT_JAVA_CONTAINER, resourceMap);
+					}
+				}
+				else {
+					reportStatus(ISingleRootStatus.SOURCE_PATH_NOT_FOUND, resourceMap);
+				}
+			}
+			else {
+				reportStatus(ISingleRootStatus.RUNTIME_PATH_NOT_ROOT_OR_WEBINF_CLASSES, resourceMap);
+			}
+			if (INCLUDE_FLAG == NONE) return false;
+		}
+		// Make sure only one of the maps is the content root, and that at least one is for the java folder
+		if (rootValidMaps != 1) {
+			if (rootValidMaps < 1) {
+				reportStatus(ISingleRootStatus.ONE_CONTENT_ROOT_REQUIRED);
+			}
+			else if (rootValidMaps > 1) {
+				reportStatus(ISingleRootStatus.ONLY_1_CONTENT_ROOT_ALLOWED);
+			}
+			if (INCLUDE_FLAG == NONE) return false;
+		}
+		if (javaValidRoots < 1) {
+			reportStatus(ISingleRootStatus.ATLEAST_1_JAVA_SOURCE_REQUIRED);
+		}		
+		return INCLUDE_FLAG == NONE ? false : true;
+	}
+	
+	/**
+	 * Checks if the path argument exists relative to this workspace root.
+	 * 
+	 * @param a workspace relative full path
+	 * @return is path in the workspace?
+	 */
+	private boolean exists(IPath path) {
+		IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
+		return workspaceRoot.exists(path);
+	}
+
+	private IProject getProject() {
+		return component.getProject();
+	}
+
+	public boolean isSingleJavaOutputNonSource() {
+		return isSingleJavaOutputNonSource;
+	}
+	
+	public void reportStatus(int code) {
+		reportStatus(code, null, null);
+	}
+	
+	public void reportStatus(int code, ComponentResource resource) {
+		reportStatus(code, resource, null);
+	}
+	
+	public void reportStatus(int code, ComponentResource resource, String message) {
+		ISingleRootStatus status = new SingleRootStatus(code, resource, message);
+		if (status.getSeverity() == IStatus.ERROR) {
+			if ((INCLUDE_FLAG & INCLUDE_FIRST_ERROR) != 0) {
+				INCLUDE_FLAG = NONE;
+				addStatus(status);
+			}
+			else if ((INCLUDE_FLAG & INCLUDE_ERRORS) != 0) {
+				addStatus(status);
+			}
+		}
+		else if (status.getSeverity() == IStatus.WARNING && (INCLUDE_FLAG & WARNINGS) != 0) {
+			addStatus(status);
+		}
+		else if (status.getSeverity() == IStatus.INFO && (INCLUDE_FLAG & INFO) != 0) { 
+			addStatus(status);
+		}
+	}
+
+	public IStatus getStatus() {
+		if (wrapperStatus != null) {
+			return wrapperStatus;
+		}
+		return Status.OK_STATUS;
+	}
+
+	private void addStatus(ISingleRootStatus status) {
+		if (wrapperStatus == null) {
+			wrapperStatus = new MultiStatus(J2EEPlugin.PLUGIN_ID, 0, new IStatus[] { status }, null, null);
+		}
+		else {
+			wrapperStatus.add(status);
+		}
+	}
+	
+}
diff --git a/plugins/org.eclipse.jst.j2ee/j2eeplugin/org/eclipse/jst/j2ee/internal/deployables/J2EEFlexProjDeployable.java b/plugins/org.eclipse.jst.j2ee/j2eeplugin/org/eclipse/jst/j2ee/internal/deployables/J2EEFlexProjDeployable.java
index a0d2b97..8ed0641 100644
--- a/plugins/org.eclipse.jst.j2ee/j2eeplugin/org/eclipse/jst/j2ee/internal/deployables/J2EEFlexProjDeployable.java
+++ b/plugins/org.eclipse.jst.j2ee/j2eeplugin/org/eclipse/jst/j2ee/internal/deployables/J2EEFlexProjDeployable.java
@@ -17,17 +17,13 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Properties;
 import java.util.Set;
 
 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;
@@ -51,7 +47,7 @@
 import org.eclipse.jst.j2ee.internal.plugin.IJ2EEModuleConstants;
 import org.eclipse.jst.j2ee.internal.plugin.J2EEPlugin;
 import org.eclipse.jst.j2ee.internal.project.J2EEProjectUtilities;
-import org.eclipse.jst.j2ee.project.JavaEEProjectUtilities;
+import org.eclipse.jst.j2ee.project.SingleRootUtil;
 import org.eclipse.jst.jee.util.internal.JavaEEQuickPeek;
 import org.eclipse.jst.server.core.IApplicationClientModule;
 import org.eclipse.jst.server.core.IConnectorModule;
@@ -61,10 +57,6 @@
 import org.eclipse.jst.server.core.IWebModule;
 import org.eclipse.wst.common.componentcore.ArtifactEdit;
 import org.eclipse.wst.common.componentcore.ComponentCore;
-import org.eclipse.wst.common.componentcore.internal.ComponentResource;
-import org.eclipse.wst.common.componentcore.internal.Property;
-import org.eclipse.wst.common.componentcore.internal.StructureEdit;
-import org.eclipse.wst.common.componentcore.internal.WorkbenchComponent;
 import org.eclipse.wst.common.componentcore.internal.resources.VirtualArchiveComponent;
 import org.eclipse.wst.common.componentcore.internal.util.ComponentUtilities;
 import org.eclipse.wst.common.componentcore.internal.util.IModuleConstants;
@@ -86,13 +78,13 @@
 	protected static final IPath WEB_CLASSES_PATH = new Path(J2EEConstants.WEB_INF_CLASSES);
 	protected static final IPath MANIFEST_PATH = new Path(J2EEConstants.MANIFEST_URI);
 	protected static IPath WEBLIB = new Path(J2EEConstants.WEB_INF_LIB).makeAbsolute();
-	private static String USE_SINGLE_ROOT_PROPERTY = "useSingleRoot"; //$NON-NLS-1$
 	protected IPackageFragmentRoot[] cachedSourceContainers;
 	protected IContainer[] cachedOutputContainers;
 	protected HashMap cachedOutputMappings;
 	protected HashMap cachedSourceOutputPairs;
 	protected List classpathComponentDependencyURIs = new ArrayList();
 	private boolean isSingleJavaOutputNonSource = false;
+	private SingleRootUtil singleUtil;
 
 	/**
 	 * Constructor for J2EEFlexProjDeployable.
@@ -829,62 +821,9 @@
 		}
 	}
 	
-    /*
-     * If the resource to check is a file then this method will return true if the file is linked. If the resource to
-     * check is a folder then this method will return true if it, any of its sub directories, or any file contained
-     * with-in this directory of any of it's sub directories are linked. Otherwise false is returned.
-     */
-    private boolean hasLinkedContent(final IResource resourceToCheck) throws CoreException {
+   
 
-    	if ((resourceToCheck != null) && resourceToCheck.isAccessible()) {
-    		// skip non-accessible files
-    		if (resourceToCheck.isLinked()) {
-    			return true;
-    		}
-    		else {
-    			switch (resourceToCheck.getType()) {
-    				case IResource.FOLDER:
-    					// recursively check sub directory contents
-    					final IResource[] subDirContents = ((IFolder) resourceToCheck).members();
-    					for (int i = 0; i < subDirContents.length; i++) {
-    						if (hasLinkedContent(subDirContents[i])) {
-    							return true;
-    						}
-    					}
-    					break;
-    				case IResource.FILE:
-    					return resourceToCheck.isLinked();
-    				default:
-    					// skip as we only care about files and folders
-    					break;
-    			}
-    		}
-    	}
-    	return false;
-    }
-
-    /*
-     * This method returns true if the root folders of this component have any linked resources (folder or file);
-     * Otherwise false is returned.
-     */
-    private boolean rootFoldersHaveLinkedContent() {
-
-    	if (this.component != null) {
-    		final IContainer[] rootFolders = this.component.getRootFolder().getUnderlyingFolders();
-    		for (int i = 0; i < rootFolders.length; i++) {
-    			try {
-    				boolean hasLinkedContent = this.hasLinkedContent(rootFolders[i]);
-    				if (hasLinkedContent) {
-    					return true;
-    				}
-    			}
-    			catch (CoreException coreEx) {
-    				J2EEPlugin.logError(coreEx);
-    			}
-    		}
-    	}
-    	return false;
-    }
+   
 	
 	/**
 	 * Returns <code>true</code> if this module has a simple structure based on a
@@ -903,207 +842,20 @@
 	 *    <code>false</code> otherwise
 	 */
 	public boolean isSingleRootStructure() {
-		isSingleJavaOutputNonSource = false;
-		StructureEdit edit = null;
-		try {
-			edit = StructureEdit.getStructureEditForRead(getProject());
-			if (edit == null || edit.getComponent() == null)
-				return false;
-			WorkbenchComponent wbComp = edit.getComponent();
-			List resourceMaps = wbComp.getResources();
-			
-			// 229650 - check to see if the property 'useSingleRoot' is defined. If it is set and
-			// the value of the property is true then it will override the logic checks below
-			final List componentProperties = wbComp.getProperties();
-			if (componentProperties != null) {
-				final Iterator componentPropertiesIterator = componentProperties.iterator();
-				while (componentPropertiesIterator.hasNext()) {
-					Property wbProperty = (Property) componentPropertiesIterator.next();
-					if (J2EEFlexProjDeployable.USE_SINGLE_ROOT_PROPERTY.equals(wbProperty.getName())) {
-						boolean useSingleRoot = Boolean.valueOf(wbProperty.getValue()).booleanValue();
-						if (useSingleRoot) {
-							return true;
-						}
-					}
-				}
-			}
-			
-			if (J2EEProjectUtilities.isEARProject(getProject())) {
-				// Always return false for EARs so that members for EAR are always calculated and j2ee modules
-				// are filtered out
-				return false;
-			} else if (J2EEProjectUtilities.isDynamicWebProject(getProject())) {
-				// if there are any linked resources then this is not a singleroot module
-				if (this.rootFoldersHaveLinkedContent()) {
-					return false;
-				}
-				// Ensure there are only basic component resource mappings -- one for the content folder 
-				// and any for src folders mapped to WEB-INF/classes
-				if (hasDefaultWebResourceMappings(resourceMaps)) {
-					// Verify only one java output folder
-					if (getJavaOutputFolders().length==1) {
-						// Verify the java output folder is to <content root>/WEB-INF/classes
-						IPath javaOutputPath = getJavaOutputFolders()[0].getProjectRelativePath();
-						IPath compRootPath = component.getRootFolder().getUnderlyingFolder().getProjectRelativePath();
-						if (compRootPath.append(J2EEConstants.WEB_INF_CLASSES).equals(javaOutputPath)) 
-							return true;
-					}
-				}
-				return false;
-			} else if (JavaEEProjectUtilities.isEJBProject(getProject()) || JavaEEProjectUtilities.isJCAProject(getProject())
-					|| JavaEEProjectUtilities.isApplicationClientProject(getProject()) || JavaEEProjectUtilities.isUtilityProject(getProject())) {
-				// if there are any linked resources then this is not a singleroot module
-				if (this.rootFoldersHaveLinkedContent()) {
-					return false;
-				}
-				// Ensure there are only source folder component resource mappings to the root content folder
-				if (isRootResourceMapping(resourceMaps,false)) {
-					IContainer[] javaOutputFolders = getJavaOutputFolders();
-					// Verify only one java outputfolder
-					if (javaOutputFolders.length==1) {
-						// By the time we get here we know: for any folders defined as source in the 
-						// .component file that they are also java source folders.
-						if (JavaEEProjectUtilities.isUtilityProject(getProject()) || 
-								JavaEEProjectUtilities.isEJBProject(getProject()) || 
-								JavaEEProjectUtilities.isApplicationClientProject(getProject())) {
-							if (! isSourceContainer(javaOutputFolders[0].getFullPath())) {
-								// The single output folder is NOT a source folder so this is single-rooted. Since the
-								// output folder (something like classes or bin) is not a source folder, JDT copies all files
-								// (including non Java files) to this folder, so every resource needed at runtime is located 
-								// in a single directory.
-								isSingleJavaOutputNonSource = true;
-								return true;
-							} else {
-// Don't implement at this time. Currently, we claim single-rooted when ejbModlule is the output folder.  However,
-// we know this is not true because it cannot contain non Java files unless it is the only source folder. But, fixing
-// at this time would break all current users.
-//								// The single output folder IS a source folder. If there is more than one source folder
-//								// then this cannot be single-rooted because JDT does NOTcopy non Java resources into the
-//								// output folder when it is a source folder.
-//								if (getSourceContainers().length > 1) {
-//									return false;
-//								} else {
-//									// There is only one source folder and since the output folder is a source folder
-//									// this is single-rooted.
-//									return true;
-//								}
-							}
-						}
-//						// At this point for utility projects, this project is optimized, we can just use the output folder
-//						if (J2EEProjectUtilities.isUtilityProject(getProject()))
-//							return true;
-						// Verify the java output folder is the same as one of the content roots
-						IPath javaOutputPath = getJavaOutputFolders()[0].getProjectRelativePath();
-						IContainer[] rootFolders = component.getRootFolder().getUnderlyingFolders();
-						for (int i=0; i<rootFolders.length; i++) {
-							IPath compRootPath = rootFolders[i].getProjectRelativePath();
-							if (javaOutputPath.equals(compRootPath))
-								return true;
-						}
-					}
-				}
-				return false;
-			}
-			return true;
-		} finally {
-			if (edit !=null)
-				edit.dispose();
-		}
+		boolean isSingleRoot = getSingleUtil().isSingleRoot();
+		isSingleJavaOutputNonSource = getSingleUtil().isSingleJavaOutputNonSource();
+		return isSingleRoot;
 	}
 	
-	/**
-	 * Ensure that any component resource mappings are for source folders and 
-	 * that they map to the root content folder
-	 * 
-	 * @param resourceMaps
-	 * @return boolean
-	 */
-	private boolean isRootResourceMapping(List resourceMaps, boolean isForEAR) {
-		// If the list is empty, return false
-		if (resourceMaps.size()<1)
-			return false;
-		
-		for (int i=0; i<resourceMaps.size(); i++) {
-			ComponentResource resourceMap = (ComponentResource) resourceMaps.get(i);
-			// Verify it maps to "/" for the content root
-			if (!resourceMap.getRuntimePath().equals(Path.ROOT))
-				return false;
-			// If this is not for an EAR, verify it is also a src container
-			if (!isForEAR) {
-				IResource sourceResource = getProject().findMember(resourceMap.getSourcePath());
-				if(sourceResource != null && sourceResource.exists()){
-					IPath sourcePath = getProject().getFullPath().append(resourceMap.getSourcePath());
-					if (!isSourceContainer(sourcePath))
-						return false;
-				}
-			}
+	private SingleRootUtil getSingleUtil() {
+		if (singleUtil == null) {
+			singleUtil = new SingleRootUtil(component);
 		}
-		return true;
-	}
-	
-	/**
-	 * Checks if the path argument is to a source container for the project.
-	 * 
-	 * @param a workspace relative full path
-	 * @return is path a source container?
-	 */
-	private boolean isSourceContainer(IPath path) {
-		IPackageFragmentRoot[] srcContainers = getSourceContainers();
-		for (int i=0; i<srcContainers.length; i++) {
-			if (srcContainers[i].getPath().equals(path))
-				return true;
-		}
-		return false;
+		return singleUtil;
 	}
 
-	/**
-	 * Checks if the path argument exists relative to this workspace root.
-	 * 
-	 * @param a workspace relative full path
-	 * @return is path in the workspace?
-	 */
-	private boolean exists(IPath path) {
-		IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
-		return workspaceRoot.exists(path);
-	}
 
 	/**
-	 * Ensure the default web setup is correct with one resource map and any number of java 
-	 * resource maps to WEB-INF/classes
-	 * 
-	 * @param resourceMaps
-	 * @return boolean
-	 */
-	private boolean hasDefaultWebResourceMappings(List resourceMaps) {
-		int rootValidMaps = 0;
-		int javaValidRoots = 0;
-		
-		// If there aren't at least 2 maps, return false
-		if (resourceMaps.size()<2)
-			return false;
-		
-		IPath webInfClasses = new Path(J2EEConstants.WEB_INF_CLASSES).makeAbsolute();
-		for (int i=0; i<resourceMaps.size(); i++) {
-			ComponentResource resourceMap = (ComponentResource) resourceMaps.get(i);
-			IPath sourcePath = getProject().getFullPath().append(resourceMap.getSourcePath());
-			
-			// Verify if the map is for the content root
-			if (resourceMap.getRuntimePath().equals(Path.ROOT)) {
-				rootValidMaps++;
-			// Verify if the map is for a java src folder and is mapped to "WEB-INF/classes"
-			} else if (resourceMap.getRuntimePath().equals(webInfClasses) && isSourceContainer(sourcePath)) {
-				javaValidRoots++;
-			// Otherwise we bail because we have a non optimized map
-			// Except if  the source path is to a nonexistent resource then just ignore the map and keep trying
-			} else if (exists(sourcePath)) {
-				return false;
-			}
-		}
-		// Make sure only one of the maps is the content root, and that at least one is for the java folder
-		return rootValidMaps==1 && javaValidRoots>0;
-	}
-	
-	/**
 	 * This method is added for performance reasons. It can ONLY be called when the project is single root.
 	 * @see isSingleRootStructure().  If the project has an output folder that is also a source folder then,
 	 * it assumes the virtual component is not using any flexible features and is in a standard J2EE format
@@ -1115,7 +867,7 @@
 	 */
 	private IModuleResource[] getOptimizedMembers() throws CoreException {
 		if (component != null) {
-			if (isSingleJavaOutputNonSource) {
+			if (isSingleJavaOutputNonSource ) {
 				// We determined when testing for a single root structure that this project has
 				// one output folder and that output folder is not a source folder. Since the
 				// output folder (for example, classes or bin) is not a source folder, JDT copies all files