/*******************************************************************************
 * Copyright (c) 2004, 2015 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.launching.sourcelookup.containers;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.resources.IContainer;
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.debug.core.sourcelookup.ISourceContainer;
import org.eclipse.debug.core.sourcelookup.ISourceContainerType;
import org.eclipse.debug.core.sourcelookup.containers.CompositeSourceContainer;
import org.eclipse.debug.core.sourcelookup.containers.FolderSourceContainer;
import org.eclipse.debug.core.sourcelookup.containers.ProjectSourceContainer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.internal.launching.LaunchingPlugin;

/**
 * Java project source container. Searches for source in a project's
 * source folders.
 * <p>
 * This class may be instantiated.
 * </p>
 *
 * @since 3.0
 * @noextend This class is not intended to be sub-classed by clients.
 */
public class JavaProjectSourceContainer extends CompositeSourceContainer {

	// Java project
	private IJavaProject fProject;
	// Source folders
	private ISourceContainer[] fSourceFolders;
	// Generic project container
	private ISourceContainer[] fOthers;

	private static String[] fgJavaExtensions = null;

	/**
	 * initialize java file extensions
	 */
	static {
		String[] extensions = JavaCore.getJavaLikeExtensions();
		fgJavaExtensions = new String[extensions.length];
		for (int i = 0; i < extensions.length; i++) {
			String ext = extensions[i];
			fgJavaExtensions[i] = '.' + ext;
		}
	}

	/**
	 * Unique identifier for Java project source container type
	 * (value <code>org.eclipse.jdt.launching.sourceContainer.javaProject</code>).
	 */
	public static final String TYPE_ID = LaunchingPlugin.getUniqueIdentifier() + ".sourceContainer.javaProject";   //$NON-NLS-1$

	/**
	 * Constructs a source container on the given Java project.
	 *
	 * @param project project to look for source in
	 */
	public JavaProjectSourceContainer(IJavaProject project) {
		fProject = project;
	}
	/* (non-Javadoc)
	 * @see org.eclipse.debug.internal.core.sourcelookup.ISourceContainer#getName()
	 */
	@Override
	public String getName() {
		return fProject.getElementName();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.internal.core.sourcelookup.ISourceContainer#getType()
	 */
	@Override
	public ISourceContainerType getType() {
		return getSourceContainerType(TYPE_ID);
	}

	/**
	 * Returns the Java project associated with this source container.
	 *
	 * @return Java project
	 */
	public IJavaProject getJavaProject() {
		return fProject;
	}
	/* (non-Javadoc)
	 * @see org.eclipse.debug.internal.core.sourcelookup.containers.CompositeSourceContainer#createSourceContainers()
	 */
	@Override
	protected ISourceContainer[] createSourceContainers() throws CoreException {
		List<ISourceContainer> containers = new ArrayList<>();
		IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
		if (fProject.getProject().isOpen()) {
			IClasspathEntry[] entries = fProject.getRawClasspath();
			for (int i = 0; i < entries.length; i++) {
				IClasspathEntry entry = entries[i];
				switch (entry.getEntryKind()) {
					case IClasspathEntry.CPE_SOURCE:
						IPath path = entry.getPath();
						IResource resource = root.findMember(path);
						if (resource instanceof IContainer) {
							containers.add(new FolderSourceContainer((IContainer)resource, false));
						}
						break;
				}
			}
		}
		// cache the Java source folders to search for java like files in
		fSourceFolders = containers.toArray(new ISourceContainer[containers.size()]);
		ISourceContainer theProject = new ProjectSourceContainer(fProject.getProject(), false);
		fOthers = new ISourceContainer[] {theProject};
		containers.add(theProject);
		return containers.toArray(new ISourceContainer[containers.size()]);
	}
	/* (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (obj instanceof JavaProjectSourceContainer) {
			return getJavaProject().equals(((JavaProjectSourceContainer)obj).getJavaProject());
		}
		return super.equals(obj);
	}
	/* (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		return getJavaProject().hashCode();
	}
	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.sourcelookup.ISourceContainer#findSourceElements(java.lang.String)
	 */
	@Override
	public Object[] findSourceElements(String name) throws CoreException {
		// force container initialization
		getSourceContainers();

		if (isJavaLikeFileName(name)) {
			// only look in source folders
			Object[] objects = findSourceElements(name, fSourceFolders);
			List<Object> filtered = null;
			for (int i = 0; i < objects.length; i++) {
				Object object = objects[i];
				if (object instanceof IResource) {
					if (!getJavaProject().isOnClasspath((IResource)object)) {
						if (filtered == null) {
							filtered = new ArrayList<>(objects.length);
							for (int j = 0; j < objects.length; j++) {
								filtered.add(objects[j]);
							}
						}
						filtered.remove(object);
					}
				}
			}
			if (filtered == null) {
				return objects;
			}
			return filtered.toArray();
		}
		// look elsewhere if not a java like file
		return findSourceElements(name, fOthers);
	}
	@Override
	public void dispose() {
		fSourceFolders = null;
		fOthers = null;
		super.dispose();
	}

	private boolean isJavaLikeFileName(String name) {
		for (int i = 0; i < fgJavaExtensions.length; i++) {
			String ext = fgJavaExtensions[i];
			if (name.endsWith(ext)) {
				return true;
			}
		}
		return false;
	}
}
