blob: df45cbc4a4ae39d34b4d4fa167e777a9a11471c4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2017 1C-Soft LLC.
*
* 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/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Vladimir Piskarev (1C) - initial API and implementation
*******************************************************************************/
package org.eclipse.handly.internal.examples.jmodel;
import java.lang.reflect.Array;
import java.util.Arrays;
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.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
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.core.runtime.Path;
import org.eclipse.handly.examples.jmodel.ICompilationUnit;
import org.eclipse.handly.examples.jmodel.IJavaElement;
import org.eclipse.handly.examples.jmodel.IJavaElementDelta;
import org.eclipse.handly.examples.jmodel.IJavaProject;
import org.eclipse.handly.examples.jmodel.IPackageFragmentRoot;
import org.eclipse.handly.examples.jmodel.JavaModelCore;
import org.eclipse.handly.model.IElement;
import org.eclipse.handly.model.IElementDelta;
import org.eclipse.handly.model.impl.IElementImplExtension;
import org.eclipse.handly.model.impl.support.Body;
import org.eclipse.handly.model.impl.support.IElementDeltaBuilder;
import org.eclipse.jdt.core.IClasspathEntry;
/**
* This class is used by the <code>JavaModelManager</code> to convert
* resource deltas into Java element deltas. It also does some processing
* on the Java elements involved (e.g. closing them).
*/
class DeltaProcessor
implements IResourceDeltaVisitor
{
private final DeltaProcessingState state;
private final IElementDeltaBuilder builder;
DeltaProcessor(DeltaProcessingState state)
{
this.state = state;
this.builder = new JavaElementDelta.Builder(new JavaElementDelta(
state.getJavaModel()));
}
/**
* Returns the Java element delta built from the resource delta.
*
* @return Java element delta (never <code>null</code>)
*/
IElementDelta getDelta()
{
return builder.getDelta();
}
@Override
public boolean visit(IResourceDelta delta) throws CoreException
{
switch (delta.getResource().getType())
{
case IResource.ROOT:
return processRoot(delta);
case IResource.PROJECT:
return processProject(delta);
case IResource.FOLDER:
return processFolder(delta);
case IResource.FILE:
return processFile(delta);
default:
return true;
}
}
private boolean processRoot(IResourceDelta delta) throws CoreException
{
state.initOldClasspathInfo();
state.initOldJavaProjectNames();
if ((delta.getFlags() & IResourceDelta.MARKERS) != 0)
builder.markersChanged(state.getJavaModel(),
delta.getMarkerDeltas());
return true;
}
private boolean processProject(IResourceDelta delta) throws CoreException
{
switch (delta.getKind())
{
case IResourceDelta.ADDED:
return processAddedProject(delta);
case IResourceDelta.REMOVED:
return processRemovedProject(delta);
case IResourceDelta.CHANGED:
return processChangedProject(delta);
default:
return true;
}
}
private boolean processAddedProject(IResourceDelta delta)
throws CoreException
{
IProject project = (IProject)delta.getResource();
if (project.hasNature(IJavaProject.NATURE_ID))
{
JavaProject javaProject = (JavaProject)JavaModelCore.create(
project);
addToModel(javaProject);
translateAddedDelta(delta, javaProject);
state.classpathChanged(javaProject, false);
}
else
{
addResourceDelta(state.getJavaModel(), delta);
}
return false;
}
private boolean processRemovedProject(IResourceDelta delta)
throws CoreException
{
IProject project = (IProject)delta.getResource();
if (wasJavaProject(project))
{
JavaProject javaProject = (JavaProject)JavaModelCore.create(
project);
removeFromModel(javaProject);
translateRemovedDelta(delta, javaProject);
state.classpathChanged(javaProject, true);
}
else
{
addResourceDelta(state.getJavaModel(), delta);
}
return false;
}
private boolean processChangedProject(IResourceDelta delta)
throws CoreException
{
IProject project = (IProject)delta.getResource();
JavaProject javaProject = (JavaProject)JavaModelCore.create(project);
if ((delta.getFlags() & IResourceDelta.OPEN) != 0)
{
if (project.isOpen())
{
if (project.hasNature(IJavaProject.NATURE_ID))
{
addToModel(javaProject);
builder.changed(javaProject, IJavaElementDelta.F_OPEN);
state.classpathChanged(javaProject, false);
}
}
else
{
if (wasJavaProject(project))
{
removeFromModel(javaProject);
builder.changed(javaProject, IJavaElementDelta.F_OPEN);
state.classpathChanged(javaProject, true);
}
}
addResourceDelta(state.getJavaModel(), delta);
return false;
}
boolean isJavaProject = project.hasNature(IJavaProject.NATURE_ID);
if ((delta.getFlags() & IResourceDelta.DESCRIPTION) != 0)
{
boolean wasJavaProject = wasJavaProject(project);
if (wasJavaProject != isJavaProject)
{
// Java nature has been added or removed
if (isJavaProject)
{
addToModel(javaProject);
builder.added(javaProject);
state.classpathChanged(javaProject, false);
}
else
{
removeFromModel(javaProject);
builder.removed(javaProject);
state.classpathChanged(javaProject, true);
}
addResourceDelta(state.getJavaModel(), delta);
return false; // when Java nature is added/removed don't process children
}
else
{
if (isJavaProject)
{
builder.changed(javaProject,
IJavaElementDelta.F_DESCRIPTION);
}
}
}
if (isJavaProject)
{
if ((delta.getFlags() & IResourceDelta.MARKERS) != 0)
builder.markersChanged(javaProject, delta.getMarkerDeltas());
if ((delta.getFlags() & IResourceDelta.SYNC) != 0)
builder.changed(javaProject, IJavaElementDelta.F_SYNC);
Body parentBody = findBody(javaProject.getParent());
IElement[] children = parentBody.getChildren();
if (!Arrays.asList(children).contains(javaProject))
addToModel(javaProject); // in case the project was removed then added then changed
checkClasspathChange(delta);
return true;
}
else
{
addResourceDelta(state.getJavaModel(), delta);
return false;
}
}
private void checkClasspathChange(IResourceDelta projectDelta)
{
IResourceDelta delta = projectDelta.findMember(new Path(".classpath")); //$NON-NLS-1$
if (delta == null || delta.getResource().getType() != IResource.FILE)
return;
JavaProject javaProject = (JavaProject)JavaModelCore.create(
delta.getResource().getProject());
switch (delta.getKind())
{
case IResourceDelta.CHANGED:
int flags = delta.getFlags();
if ((flags & IResourceDelta.CONTENT) == 0)
break;
//$FALL-THROUGH$
case IResourceDelta.ADDED:
case IResourceDelta.REMOVED:
if (state.classpathChanged(javaProject, false))
{
builder.changed(javaProject,
IJavaElementDelta.F_CLASSPATH_CHANGED);
close(javaProject);
}
}
}
private boolean processFolder(IResourceDelta delta) throws CoreException
{
switch (delta.getKind())
{
case IResourceDelta.ADDED:
return processAddedFolder(delta);
case IResourceDelta.REMOVED:
return processRemovedFolder(delta);
case IResourceDelta.CHANGED:
return processChangedFolder(delta);
default:
return false;
}
}
private boolean processAddedFolder(IResourceDelta delta)
{
IFolder folder = (IFolder)delta.getResource();
IJavaElement element = state.createElement(folder, false);
if (element != null)
{
addToModel(element);
translateAddedDelta(delta, element);
return true;
}
else
{
addResourceDelta(state.createElement(
delta.getResource().getParent(), false), delta);
return false;
}
}
private boolean processRemovedFolder(IResourceDelta delta)
{
IFolder folder = (IFolder)delta.getResource();
IJavaElement element = state.createElement(folder, true);
if (element != null)
{
removeFromModel(element);
translateRemovedDelta(delta, element);
return true;
}
else
{
addResourceDelta(state.createElement(
delta.getResource().getParent(), false), delta);
return false;
}
}
private boolean processChangedFolder(IResourceDelta delta)
{
IFolder folder = (IFolder)delta.getResource();
IJavaElement element = state.createElement(folder, false);
if (element != null)
{
if ((delta.getFlags() & IResourceDelta.MARKERS) != 0)
builder.markersChanged(element, delta.getMarkerDeltas());
if ((delta.getFlags() & IResourceDelta.SYNC) != 0)
builder.changed(element, IJavaElementDelta.F_SYNC);
return true;
}
else
{
addResourceDelta(state.createElement(
delta.getResource().getParent(), false), delta);
return false;
}
}
private boolean processFile(IResourceDelta delta)
{
switch (delta.getKind())
{
case IResourceDelta.ADDED:
return processAddedFile(delta);
case IResourceDelta.REMOVED:
return processRemovedFile(delta);
case IResourceDelta.CHANGED:
return processChangedFile(delta);
default:
return false;
}
}
private boolean processAddedFile(IResourceDelta delta)
{
IFile file = (IFile)delta.getResource();
IJavaElement element = state.createElement(file, false);
if (element != null)
{
addToModel(element);
translateAddedDelta(delta, element);
}
else
{
addResourceDelta(state.createElement(
delta.getResource().getParent(), false), delta);
}
return false;
}
private boolean processRemovedFile(IResourceDelta delta)
{
IFile file = (IFile)delta.getResource();
IJavaElement element = state.createElement(file, true);
if (element != null)
{
removeFromModel(element);
translateRemovedDelta(delta, element);
}
else
{
addResourceDelta(state.createElement(
delta.getResource().getParent(), false), delta);
}
return false;
}
private boolean processChangedFile(IResourceDelta delta)
{
IFile file = (IFile)delta.getResource();
IJavaElement element = state.createElement(file, false);
if (element != null)
{
if ((delta.getFlags() & ~(IResourceDelta.MARKERS
| IResourceDelta.SYNC)) != 0)
contentChanged(element);
if ((delta.getFlags() & IResourceDelta.MARKERS) != 0)
builder.markersChanged(element, delta.getMarkerDeltas());
if ((delta.getFlags() & IResourceDelta.SYNC) != 0)
builder.changed(element, IJavaElementDelta.F_SYNC);
}
else
{
addResourceDelta(state.createElement(
delta.getResource().getParent(), false), delta);
}
return false;
}
private boolean wasJavaProject(IProject project)
{
return state.getOldJavaProjectNames().contains(project.getName());
}
private void addToModel(IJavaElement element)
{
Body parentBody = findBody(element.getParent());
if (parentBody != null)
{
// Insert package fragment roots in the same order as in classpath
if (element instanceof PackageFragmentRoot)
{
addPackageFragmentRoot(parentBody,
(PackageFragmentRoot)element);
}
else
parentBody.addChild(element);
}
close(element);
}
private void addPackageFragmentRoot(Body parentBody,
PackageFragmentRoot root)
{
IElement[] roots = parentBody.getChildren();
if (roots.length == 0)
{
parentBody.addChild(root);
return;
}
IClasspathEntry[] classpath;
try
{
classpath = root.getParent().getRawClasspath();
}
catch (CoreException e)
{
Activator.log(e.getStatus());
parentBody.addChild(root);
return;
}
IPath rootPath = root.getPath();
int indexToInsert = -1;
int lastComparedIndex = -1;
int i = 0, j = 0;
while (i < roots.length && j < classpath.length)
{
IPath entryPath = classpath[j].getPath();
if (lastComparedIndex != j && rootPath.equals(entryPath))
{
indexToInsert = i;
break;
}
lastComparedIndex = j;
if (((IPackageFragmentRoot)roots[i]).getPath().equals(entryPath))
i++;
else
j++;
}
for (; i < roots.length; i++)
{
// If the new root is already among the children, no need to proceed further
if (roots[i].equals(root))
return;
if (!((IPackageFragmentRoot)roots[i]).getPath().equals(rootPath))
break;
}
if (indexToInsert < 0)
parentBody.addChild(root);
else
{
int newLength = roots.length + 1;
IElement[] newChildren = (IElement[])Array.newInstance(
roots.getClass().getComponentType(), newLength);
if (indexToInsert > 0)
System.arraycopy(roots, 0, newChildren, 0, indexToInsert);
newChildren[indexToInsert] = root;
System.arraycopy(roots, indexToInsert, newChildren, indexToInsert
+ 1, newLength - indexToInsert - 1);
parentBody.setChildren(newChildren);
}
}
private void removeFromModel(IJavaElement element)
{
Body parentBody = findBody(element.getParent());
if (parentBody != null)
parentBody.removeChild(element);
close(element);
if (element instanceof IJavaProject)
JavaModelManager.INSTANCE.removePerProjectInfo(
((IJavaProject)element).getProject());
}
private void translateAddedDelta(IResourceDelta delta, IJavaElement element)
{
if ((delta.getFlags() & IResourceDelta.MOVED_FROM) == 0) // regular addition
{
builder.added(element);
}
else
{
IJavaElement movedFromElement = state.createElement(getResource(
delta.getMovedFromPath(), delta.getResource().getType()), true);
if (movedFromElement == null)
builder.added(element);
else
builder.movedTo(element, movedFromElement);
}
}
private void translateRemovedDelta(IResourceDelta delta,
IJavaElement element)
{
if ((delta.getFlags() & IResourceDelta.MOVED_TO) == 0) // regular removal
{
builder.removed(element);
}
else
{
IJavaElement movedToElement = state.createElement(getResource(
delta.getMovedToPath(), delta.getResource().getType()), false);
if (movedToElement == null)
builder.removed(element);
else
builder.movedFrom(element, movedToElement);
}
}
private void contentChanged(IJavaElement element)
{
if (element instanceof ICompilationUnit
&& ((ICompilationUnit)element).isWorkingCopy())
{
builder.changed(element, IJavaElementDelta.F_CONTENT
| IJavaElementDelta.F_UNDERLYING_RESOURCE);
return;
}
close(element);
builder.changed(element, IJavaElementDelta.F_CONTENT);
}
private void addResourceDelta(IJavaElement element,
IResourceDelta resourceDelta)
{
if (element == null)
return;
// reset non-Java resources if element was open
Body body = findBody(element);
if (body != null)
{
if (body instanceof JavaModelBody)
((JavaModelBody)body).setNonJavaProjects(null);
else if (body instanceof JavaProjectBody)
((JavaProjectBody)body).setNonJavaResources(null);
else if (body instanceof PackageFragmentRootBody)
((PackageFragmentRootBody)body).setNonJavaResources(null);
else if (body instanceof PackageFragmentBody)
((PackageFragmentBody)body).setNonJavaResources(null);
else
throw new AssertionError();
}
builder.addResourceDelta(element, resourceDelta);
}
private static Body findBody(IJavaElement element)
{
return (Body)((IElementImplExtension)element).findBody_();
}
private static void close(IJavaElement element)
{
((IElementImplExtension)element).close_();
}
private static IResource getResource(IPath fullPath, int resourceType)
{
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
switch (resourceType)
{
case IResource.ROOT:
return root;
case IResource.PROJECT:
return root.getProject(fullPath.lastSegment());
case IResource.FOLDER:
return root.getFolder(fullPath);
case IResource.FILE:
return root.getFile(fullPath);
default:
return null;
}
}
}