blob: ea6eb3f19b7642287ac73495c3866565754ff123 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2008, 2021 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.ltk.model.core;
import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
import java.nio.channels.IllegalSelectorException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.statet.jcommons.collections.ImCollections;
import org.eclipse.statet.jcommons.collections.ImList;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.ltk.model.core.element.LtkModelElement;
import org.eclipse.statet.ltk.model.core.element.SourceStructElement;
import org.eclipse.statet.ltk.model.core.element.SourceUnit;
import org.eclipse.statet.ltk.model.core.element.WorkspaceSourceUnit;
@NonNullByDefault
public class ElementSet {
public static Set<String> getAffectedProjectNatures(final ElementSet set)
throws CoreException {
return getAffectedProjectNatures(ImCollections.newList(set));
}
public static Set<String> getAffectedProjectNatures(final List<ElementSet> sets)
throws CoreException {
final Set<String> natureIds= new HashSet<>();
for (final ElementSet set : sets) {
final Set<IProject> affectedProjects= set.getAffectedProjects();
for (final IProject project : affectedProjects) {
final String[] ids= project.getDescription().getNatureIds();
for (final String id : ids) {
natureIds.add(id);
}
}
}
return natureIds;
}
private static final int POST_INIT= 0x10000;
private static final int POST_PROCESS= 0x70000;
private final ImList<Object> initialElements;
private List<LtkModelElement<?>> modelElements= new ArrayList<>();
private List<IResource> resources= new ArrayList<>();
private int processState= 0;
private List<IResource> resourcesOwnedByElements;
private List<IFile> filesContainingElements;
public ElementSet(final List<Object> elements) {
this.initialElements= ImCollections.toList(elements);
init(elements);
if (countElements() == this.initialElements.size()) {
this.processState= POST_INIT;
}
else {
this.processState= -POST_INIT;
}
}
public ElementSet(final Object... elements) {
this(ImCollections.newList(elements));
}
protected void init(final List<Object> elements) {
for (final Object o : elements) {
add(o);
}
}
protected void add(final Object o) {
if (o instanceof LtkModelElement) {
this.modelElements.add((LtkModelElement<?>)o);
return;
}
if (o instanceof IResource) {
final IResource resource = (IResource)o;
if (resource.getType() == IResource.ROOT) {
throw new IllegalArgumentException("ROOT");
}
if (this.resources == null) {
this.resources= new ArrayList<>();
}
this.resources.add((IResource)o);
return;
}
}
protected int countElements() {
return this.resources.size() + this.modelElements.size();
}
public int getElementCount() {
return countElements();
}
public boolean isOK() {
return (this.processState > 0);
}
public List<Object> getInitialObjects() {
return this.initialElements;
}
public List<IResource> getResources() {
return this.resources;
}
public List<LtkModelElement<?>> getModelElements() {
return this.modelElements;
}
public List<IResource> getResourcesOwnedByElements() {
return this.resourcesOwnedByElements;
}
public List<IFile> getFilesContainingElements() {
return this.filesContainingElements;
}
public @Nullable IResource getOwningResource(final LtkModelElement<?> element) {
if ((element.getElementType() & LtkModelElement.MASK_C12) < LtkModelElement.C12_SOURCE_CHUNK) {
IResource resource;
resource= element.getAdapter(IResource.class);
return resource;
}
return null;
}
public @Nullable IResource getResource(final LtkModelElement<?> element) {
final SourceUnit su= LtkModelUtils.getSourceUnit(element);
if (su instanceof WorkspaceSourceUnit) {
return ((WorkspaceSourceUnit)su).getResource();
}
return null;
}
public @Nullable IProject getSingleProject() {
IProject project= null;
for (final IResource resource : this.resources) {
final IProject p= resource.getProject();
if (project == null) {
project= p;
continue;
}
if (!project.equals(p)) {
return null;
}
}
for (final LtkModelElement<?> element : this.modelElements) {
final IResource resource= getResource(element);
if (resource == null) {
continue;
}
final IProject p= resource.getProject();
if (project == null) {
project= p;
continue;
}
if (!project.equals(p)) {
return null;
}
}
return project;
}
public Set<IProject> getProjects() {
final Set<IProject> projects= new HashSet<>();
for (final IResource resource : this.resources) {
projects.add(nonNullAssert(resource.getProject()));
}
for (final LtkModelElement<?> element : this.modelElements) {
final IResource resource= getResource(element);
if (resource != null) {
projects.add(nonNullAssert(resource.getProject()));
}
}
return projects;
}
public Set<IProject> getAffectedProjects() {
final Set<IProject> projects= getProjects();
final IProject[] array= projects.toArray(new IProject[projects.size()]);
for (int i= 0; i < array.length; i++) {
final IProject[] referencingProjects= array[i].getReferencingProjects();
if (referencingProjects.length > 0) {
addAffectedProjects(referencingProjects, projects);
}
}
return projects;
}
private void addAffectedProjects(final IProject[] projectToAdd, final Set<IProject> projects) {
for (int i= 0; i < projectToAdd.length; i++) {
if (projects.add(projectToAdd[i])) {
final IProject[] referencingProjects= projectToAdd[i].getReferencingProjects();
if (referencingProjects.length > 0) {
addAffectedProjects(referencingProjects, projects);
}
}
}
}
public void removeElementsWithAncestorsOnList() {
if ((this.processState & 0x10) == 0) {
removeResourcesDescendantsOfResources();
removeResourcesDescendantsOfModelElements();
removeModelElementsDescendantsOfModelElements();
this.processState |= 0x10;
}
}
private void removeResourcesDescendantsOfResources() {
final Iterator<IResource> iter= this.resources.iterator();
ITER_RESOURCE : while (iter.hasNext()) {
final IResource subResource= iter.next();
for (final IResource superResource : this.resources) {
if (isDescendantOf(subResource, superResource)) {
iter.remove();
continue ITER_RESOURCE;
}
}
}
}
private void removeResourcesDescendantsOfModelElements() {
final Iterator<IResource> iter= this.resources.iterator();
ITER_RESOURCE : while (iter.hasNext()) {
final IResource subResource= iter.next();
for (final LtkModelElement<?> superElement : this.modelElements) {
if (isDescendantOf(subResource, superElement)) {
iter.remove();
continue ITER_RESOURCE;
}
}
}
}
private void removeModelElementsDescendantsOfModelElements() {
final Iterator<LtkModelElement<?>> iter= this.modelElements.iterator();
ITER_ELEMENT : while (iter.hasNext()) {
final LtkModelElement<?> subElement= iter.next();
for (final LtkModelElement<?> superElement : this.modelElements) {
if (isDescendantOf(subElement, superElement)) {
iter.remove();
continue ITER_ELEMENT;
}
}
}
}
public boolean includes(final LtkModelElement<?> element) {
if (this.modelElements.contains(element)) {
return true;
}
for (final LtkModelElement<?> e : this.modelElements) {
if (isDescendantOf(element, e)) {
return true;
}
}
// TODO check resources
return false;
}
protected boolean isDescendantOf(final IResource subResource, final IResource superResource) {
return !subResource.equals(superResource) && superResource.getFullPath().isPrefixOf(subResource.getFullPath());
}
protected boolean isDescendantOf(final IResource subResource, final LtkModelElement<?> superElement) {
final IResource superResource= getOwningResource(superElement);
if (superResource != null) {
return isDescendantOf(subResource, superResource);
}
return false;
}
protected boolean isDescendantOf(final LtkModelElement<?> subElement, final LtkModelElement<?> superElement) {
if (subElement.equals(superElement)
|| !(subElement instanceof SourceStructElement)) {
return false;
}
SourceStructElement<?, ?> parent= ((SourceStructElement<?, ?>)subElement).getSourceParent();
while (parent != null){
if (parent.equals(superElement)) {
return true;
}
parent= parent.getSourceParent();
}
return false;
}
protected void setModelElements(final List<LtkModelElement<?>> newElements) {
if (this.processState >= POST_PROCESS) {
throw new IllegalSelectorException();
}
this.modelElements= newElements;
}
public void postProcess() {
if (this.processState < 0) {
throw new IllegalStateException();
}
if (this.processState < POST_PROCESS) {
this.resourcesOwnedByElements= new ArrayList<>(1);
this.filesContainingElements= new ArrayList<>(1);
for (final LtkModelElement<?> element : this.modelElements) {
IResource resource;
resource= getOwningResource(element);
if (resource != null) {
this.resourcesOwnedByElements.add(resource);
continue;
}
resource= getResource(element);
if (resource != null && resource.getType() == IResource.FILE) {
this.filesContainingElements.add((IFile)resource);
continue;
}
}
}
this.processState= POST_PROCESS | (this.processState & 0xffff);
}
}