blob: e0d4b11b04351492df61ff05c0aded969ff741da [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2017 xored software, Inc. 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:
* xored software, Inc. - initial API and Implementation (Alex Panchenko)
*******************************************************************************/
package org.eclipse.dltk.internal.core.builder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
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.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.dltk.core.ISourceModule;
import org.eclipse.dltk.core.builder.IRenameChange;
import org.eclipse.dltk.utils.TextUtils;
public class IncrementalProjectChange extends AbstractBuildChange
implements IResourceDeltaVisitor {
private final IResourceDelta delta;
public IncrementalProjectChange(IResourceDelta delta, IProject project,
IProgressMonitor monitor) {
super(project, monitor);
this.delta = delta;
}
@Override
public IResourceDelta getResourceDelta() {
return delta;
}
@SuppressWarnings("unchecked")
private final List<IPath>[] deletes = new List[9];
@Override
public List<IPath> getDeletes(int options) throws CoreException {
validateFlags(options, NO_RENAMES);
loadData();
if (deletes[options] == null) {
final List<IPath> paths = new ArrayList<>();
if (projectDeletes != null) {
paths.addAll(projectDeletes);
}
if (wantRenames(options) && projectRenames != null) {
paths.addAll(projectRenames.values());
}
deletes[options] = unmodifiableList(paths);
}
return deletes[options];
}
@Override
public List<IRenameChange> getRenames() throws CoreException {
loadData();
if (projectRenames != null) {
return projectRenames.getRenames();
} else {
return Collections.emptyList();
}
}
@SuppressWarnings("unchecked")
private final List<IFile>[] resources = new List[16];
@Override
public List<IFile> getResources(int options) throws CoreException {
options = validateFlags(options, ALL | NO_RENAMES | ADDED | CHANGED);
if ((options & (ADDED | CHANGED | NO_RENAMES)) == (CHANGED
| NO_RENAMES)) {
throw new IllegalArgumentException();
}
loadData();
if (resources[options] == null) {
final List<IFile> files = new ArrayList<>();
if (checkFlag(options, ADDED)) {
if (projectAdditions != null) {
files.addAll(projectAdditions
.getResources(checkFlag(options, ALL)));
}
if (wantRenames(options) && projectRenames != null) {
files.addAll(projectRenames
.getResources(checkFlag(options, ALL)));
}
}
if (checkFlag(options, CHANGED)) {
if (projectChanges != null) {
files.addAll(projectChanges
.getResources(checkFlag(options, ALL)));
}
}
resources[options] = unmodifiableList(files);
}
return resources[options];
}
@SuppressWarnings("unchecked")
private final List<ISourceModule>[] modules = new List[4];
@Override
public List<ISourceModule> getSourceModules(int options)
throws CoreException {
options = validateFlags(options, ADDED | CHANGED);
loadData();
if (modules[options] == null) {
final List<ISourceModule> m = new ArrayList<>();
if (checkFlag(options, ADDED)) {
if (projectAdditions != null) {
m.addAll(projectAdditions.getSourceModules());
}
if (projectRenames != null) {
m.addAll(projectRenames.getSourceModules());
}
}
if (checkFlag(options, CHANGED)) {
if (projectChanges != null) {
m.addAll(projectChanges.getSourceModules());
}
}
modules[options] = unmodifiableList(m);
}
return modules[options];
}
private boolean loaded = false;
protected final void loadData() throws CoreException {
if (!loaded) {
loaded = true;
delta.accept(this);
}
}
@SuppressWarnings("serial")
abstract class AbstractChangeSet<T> extends HashMap<IFile, T> {
List<ISourceModule> modules = null;
Set<IFile> realResources = null;
public Collection<? extends IFile> getAll() {
return keySet();
}
void resetDerivedContent() {
modules = null;
realResources = null;
}
List<ISourceModule> getSourceModules() {
if (modules == null) {
detectSourceModules();
}
return modules;
}
Collection<IFile> getResources() {
if (realResources == null) {
detectSourceModules();
}
return realResources;
}
Collection<? extends IFile> getResources(boolean all) {
return all ? getAll() : getResources();
}
private final void detectSourceModules() {
final List<ISourceModule> m = new ArrayList<>();
final Set<IFile> rr = new HashSet<>();
locateSourceModules(this.keySet(), m, rr);
this.modules = unmodifiableList(m);
this.realResources = unmodifiableSet(rr);
}
}
@SuppressWarnings("serial")
class ChangeSet extends AbstractChangeSet<Object> {
void add(IFile file) {
put(file, Boolean.TRUE);
}
@Override
public String toString() {
return keySet().toString();
}
}
@SuppressWarnings("serial")
class RenameChangeSet extends AbstractChangeSet<IPath> {
List<IRenameChange> renames = null;
@Override
void resetDerivedContent() {
super.resetDerivedContent();
renames = null;
}
List<IRenameChange> getRenames() {
if (renames == null) {
final List<IRenameChange> r = new ArrayList<>();
for (Map.Entry<IFile, IPath> entry : entrySet()) {
r.add(new RenameChange(entry.getValue(), entry.getKey()));
}
this.renames = unmodifiableList(r);
}
return renames;
}
}
private ChangeSet projectAdditions = null;
private ChangeSet projectChanges = null;
/**
* Project-relative paths of the deleted files.
*/
private List<IPath> projectDeletes = null;
private RenameChangeSet projectRenames = null;
protected void resetDerivedProjectChanges() {
if (projectAdditions != null) {
projectAdditions.resetDerivedContent();
}
if (projectChanges != null) {
projectChanges.resetDerivedContent();
}
if (projectRenames != null) {
projectRenames.resetDerivedContent();
}
Arrays.fill(deletes, null);
Arrays.fill(resources, null);
Arrays.fill(modules, null);
}
protected boolean addChangedResource(IFile file) throws CoreException {
Assert.isLegal(project.equals(file.getProject()));
loadData();
if (projectAdditions != null && projectAdditions.containsKey(file)) {
return false;
}
if (projectChanges == null) {
projectChanges = new ChangeSet();
}
if (!projectChanges.containsKey(file)) {
projectChanges.add(file);
resetDerivedProjectChanges();
return true;
}
return false;
}
@Override
public boolean visit(IResourceDelta delta) throws CoreException {
checkCanceled();
final IResource resource = delta.getResource();
// System.out.println(resource);
if (resource.getType() == IResource.FOLDER) {
this.monitor.subTask(Messages.ScriptBuilder_scanningProjectFolder
+ resource.getProjectRelativePath().toString());
}
if (resource.getType() == IResource.FILE) {
switch (delta.getKind()) {
case IResourceDelta.ADDED:
if ((delta.getFlags() & IResourceDelta.MOVED_FROM) != 0) {
if (projectRenames == null) {
projectRenames = new RenameChangeSet();
}
projectRenames.put((IFile) resource,
delta.getMovedFromPath().removeFirstSegments(1));
} else {
if (projectAdditions == null) {
projectAdditions = new ChangeSet();
}
projectAdditions.add((IFile) resource);
}
break;
case IResourceDelta.CHANGED:
if ((delta.getFlags() & (IResourceDelta.CONTENT
| IResourceDelta.ENCODING)) != 0) {
if (projectChanges == null) {
projectChanges = new ChangeSet();
}
projectChanges.add((IFile) resource);
}
break;
case IResourceDelta.REMOVED:
if ((delta.getFlags() & IResourceDelta.MOVED_TO) == 0) {
if (projectDeletes == null) {
projectDeletes = new ArrayList<>();
}
projectDeletes.add(delta.getProjectRelativePath());
}
break;
}
return false;
}
return true;
}
@Override
public String toString() {
try {
loadData();
} catch (CoreException e) {
return e.toString();
}
final List<String> lines = new ArrayList<>();
if (projectAdditions != null) {
lines.add("additions=" + projectAdditions.toString());
}
if (projectChanges != null) {
lines.add("changes=" + projectChanges.toString());
}
if (projectDeletes != null) {
lines.add("deletes=" + projectDeletes);
}
return TextUtils.join(lines, "\n");
}
protected Set<IPath> getAddedPaths() throws CoreException {
loadData();
final Set<IPath> paths = new HashSet<>();
if (projectAdditions != null) {
for (IFile file : projectAdditions.getAll()) {
paths.add(file.getFullPath());
}
}
if (projectRenames != null) {
for (IRenameChange rename : projectRenames.getRenames()) {
paths.add(rename.getTarget().getFullPath());
}
}
return paths;
}
/**
* Returns full paths of the deleted files.
*/
protected Collection<IPath> getDeletedPaths() throws CoreException {
loadData();
final Set<IPath> paths = new HashSet<>();
if (projectDeletes != null) {
final IPath projectPath = project.getFullPath();
for (IPath path : projectDeletes) {
paths.add(projectPath.append(path));
}
}
if (projectRenames != null) {
final IPath projectPath = project.getFullPath();
for (IRenameChange rename : projectRenames.getRenames()) {
paths.add(projectPath.append(rename.getSource()));
}
}
return paths;
}
protected Set<IPath> getChangedPaths() throws CoreException {
loadData();
final Set<IPath> paths = new HashSet<>();
if (projectAdditions != null) {
for (IFile file : projectAdditions.getAll()) {
paths.add(file.getFullPath());
}
}
if (projectChanges != null) {
for (IFile file : projectChanges.getAll()) {
paths.add(file.getFullPath());
}
}
if (projectDeletes != null) {
final IPath projectPath = project.getFullPath();
for (IPath path : projectDeletes) {
paths.add(projectPath.append(path));
}
}
if (projectRenames != null) {
final IPath projectPath = project.getFullPath();
for (IRenameChange rename : projectRenames.getRenames()) {
paths.add(projectPath.append(rename.getSource()));
paths.add(rename.getTarget().getFullPath());
}
}
return paths;
}
}