blob: f313971a60f105c7efe222be0dcf2431dd56f445 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2022 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.pde.internal.core;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.ListIterator;
import java.util.Map;
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.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.pde.core.IModel;
import org.eclipse.pde.core.IModelProviderEvent;
import org.eclipse.pde.internal.core.project.PDEProject;
import org.eclipse.team.core.RepositoryProvider;
public abstract class WorkspaceModelManager<T> extends AbstractModelManager
implements IResourceChangeListener, IResourceDeltaVisitor {
public static boolean isPluginProject(IProject project) {
if (project == null) {
return false;
}
if (project.isOpen()) {
return PDEProject.getManifest(project).exists() || PDEProject.getPluginXml(project).exists() || PDEProject.getFragmentXml(project).exists();
}
return false;
}
public static boolean isFeatureProject(IProject project) {
return project.isOpen() && PDEProject.getFeatureXml(project).exists();
}
public static boolean isBinaryProject(IProject project) {
try {
if (project.isOpen()) {
String binary = project.getPersistentProperty(PDECore.EXTERNAL_PROJECT_PROPERTY);
if (binary != null) {
RepositoryProvider provider = RepositoryProvider.getProvider(project);
return provider == null || provider instanceof BinaryRepositoryProvider;
}
}
} catch (CoreException e) {
PDECore.logException(e);
}
return false;
}
public static boolean isUnsharedProject(IProject project) {
return RepositoryProvider.getProvider(project) == null || isBinaryProject(project);
}
static class ModelChange {
IModel model;
int type;
public ModelChange(IModel model, int type) {
this.model = model;
this.type = type;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ModelChange) {
ModelChange change = (ModelChange) obj;
IProject project = change.model.getUnderlyingResource().getProject();
int type = change.type;
return model.getUnderlyingResource().getProject().equals(project) && this.type == type;
}
return false;
}
}
private Map<IProject, T> fModels = null;
private ArrayList<ModelChange> fChangedModels;
protected Map<IProject, T> getModelsMap() {
ensureModelsMapCreated();
return fModels;
}
private void ensureModelsMapCreated() {
if (fModels == null) {
fModels = Collections.synchronizedMap(new LinkedHashMap<IProject, T>());
}
}
protected synchronized void initialize() {
if (fModels != null) {
return;
}
ensureModelsMapCreated();
IProject[] projects = PDECore.getWorkspace().getRoot().getProjects();
for (IProject project : projects) {
if (isInterestingProject(project)) {
createModel(project, false);
}
}
addListeners();
}
protected abstract boolean isInterestingProject(IProject project);
protected abstract void createModel(IProject project, boolean notify);
protected abstract T removeModel(IProject project);
protected void addListeners() {
IWorkspace workspace = PDECore.getWorkspace();
workspace.addResourceChangeListener(this, IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.POST_CHANGE);
}
@Override
protected void removeListeners() {
PDECore.getWorkspace().removeResourceChangeListener(this);
super.removeListeners();
}
protected T getModel(IProject project) {
initialize();
return fModels.get(project);
}
@Override
public void resourceChanged(IResourceChangeEvent event) {
switch (event.getType()) {
case IResourceChangeEvent.POST_CHANGE :
handleResourceDelta(event.getDelta());
processModelChanges();
break;
case IResourceChangeEvent.PRE_CLOSE :
removeModel((IProject) event.getResource());
processModelChanges();
break;
}
}
private void handleResourceDelta(IResourceDelta delta) {
try {
delta.accept(this);
} catch (CoreException e) {
PDECore.logException(e);
}
}
@Override
public boolean visit(IResourceDelta delta) throws CoreException {
if (delta != null) {
final IResource resource = delta.getResource();
switch (resource.getType())
{
case IResource.ROOT:
return true;
case IResource.PROJECT: {
IProject project = (IProject) resource;
boolean addedOrOpened = delta.getKind() == IResourceDelta.ADDED
|| (delta.getFlags() & IResourceDelta.OPEN) != 0;
if (isInterestingProject(project) && addedOrOpened) {
createModel(project, true);
return false;
} else if (delta.getKind() == IResourceDelta.REMOVED) {
removeModel(project);
return false;
}
return true;
}
case IResource.FOLDER:
return isInterestingFolder((IFolder) resource);
case IResource.FILE:
// do not process
if (isContentChange(delta)) {
handleFileDelta(delta);
return false;
}
}
}
return false;
}
private boolean isContentChange(IResourceDelta delta) {
int kind = delta.getKind();
return (kind == IResourceDelta.ADDED || kind == IResourceDelta.REMOVED || (kind == IResourceDelta.CHANGED && (delta.getFlags() & IResourceDelta.CONTENT) != 0));
}
protected boolean isInterestingFolder(IFolder folder) {
return false;
}
protected abstract void handleFileDelta(IResourceDelta delta);
protected void addChange(IModel model, int eventType) {
if (model != null) {
if (fChangedModels == null) {
fChangedModels = new ArrayList<>();
}
ModelChange change = new ModelChange(model, eventType);
if (!fChangedModels.contains(change)) {
fChangedModels.add(change);
}
}
}
protected void processModelChanges() {
processModelChanges("org.eclipse.pde.core.IModelProviderEvent", fChangedModels); //$NON-NLS-1$
fChangedModels = null;
}
protected void processModelChanges(String changeId, ArrayList<ModelChange> changedModels) {
if (changedModels == null) {
return;
}
if (changedModels.isEmpty()) {
return;
}
ArrayList<IModel> added = new ArrayList<>();
ArrayList<IModel> removed = new ArrayList<>();
ArrayList<IModel> changed = new ArrayList<>();
for (ListIterator<ModelChange> li = changedModels.listIterator(); li.hasNext();) {
ModelChange change = li.next();
switch (change.type) {
case IModelProviderEvent.MODELS_ADDED :
added.add(change.model);
break;
case IModelProviderEvent.MODELS_REMOVED :
removed.add(change.model);
break;
case IModelProviderEvent.MODELS_CHANGED :
changed.add(change.model);
}
}
int type = 0;
if (!added.isEmpty()) {
type |= IModelProviderEvent.MODELS_ADDED;
}
if (!removed.isEmpty()) {
type |= IModelProviderEvent.MODELS_REMOVED;
}
if (!changed.isEmpty()) {
type |= IModelProviderEvent.MODELS_CHANGED;
}
if (type != 0) {
createAndFireEvent(changeId, type, added, removed, changed);
}
}
protected void loadModel(IModel model, boolean reload) {
IFile file = (IFile) model.getUnderlyingResource();
try (InputStream stream = new BufferedInputStream(file.getContents(true));) {
if (reload) {
model.reload(stream, false);
} else {
model.load(stream, false);
}
} catch (CoreException | IOException e) {
PDECore.logException(e);
}
}
protected void createAndFireEvent(String eventId, int type, Collection<IModel> added, Collection<IModel> removed, Collection<IModel> changed) {
if (eventId.equals("org.eclipse.pde.core.IModelProviderEvent")) { //$NON-NLS-1$
final ModelProviderEvent event = new ModelProviderEvent(this, type, added.toArray(new IModel[added.size()]), removed.toArray(new IModel[removed.size()]), changed.toArray(new IModel[changed.size()]));
fireModelProviderEvent(event);
}
}
}