| /*=============================================================================# |
| # 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_C2) < LtkModelElement.C2_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); |
| } |
| |
| } |