blob: c0ba414d908f22709525d071e8ec8e00bd137d5c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2020 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 static org.eclipse.handly.context.Contexts.EMPTY_CONTEXT;
import static org.eclipse.handly.context.Contexts.of;
import static org.eclipse.handly.context.Contexts.with;
import static org.eclipse.handly.model.Elements.BASE_SNAPSHOT;
import static org.eclipse.handly.model.Elements.FORCE_RECONCILING;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.handly.buffer.IBuffer;
import org.eclipse.handly.context.Context;
import org.eclipse.handly.context.IContext;
import org.eclipse.handly.examples.jmodel.ICompilationUnit;
import org.eclipse.handly.examples.jmodel.IImportContainer;
import org.eclipse.handly.examples.jmodel.IImportDeclaration;
import org.eclipse.handly.examples.jmodel.IJavaSourceConstruct;
import org.eclipse.handly.examples.jmodel.IJavaSourceElement;
import org.eclipse.handly.examples.jmodel.IPackageDeclaration;
import org.eclipse.handly.examples.jmodel.IType;
import org.eclipse.handly.model.impl.support.ISourceFileImplSupport;
import org.eclipse.handly.snapshot.ISnapshot;
import org.eclipse.handly.util.Property;
import org.eclipse.jdt.core.IProblemRequestor;
import org.eclipse.jdt.core.JavaConventions;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.WorkingCopyOwner;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
/**
* Implementation of {@link ICompilationUnit}.
*/
public class CompilationUnit
extends JavaElement
implements ICompilationUnit, ISourceFileImplSupport
{
static final IJavaSourceConstruct[] NO_CHILDREN =
new IJavaSourceConstruct[0];
@SuppressWarnings("restriction")
private static final WorkingCopyOwner PRIMARY_OWNER =
org.eclipse.jdt.internal.core.DefaultWorkingCopyOwner.PRIMARY;
private static final IImportDeclaration[] NO_IMPORTS =
new IImportDeclaration[0];
private final IFile file;
private final WorkingCopyOwner owner;
/**
* Constructs a handle for a Java compilation unit with the given
* parent element and the given underlying workspace file.
*
* @param parent the parent of the element (not <code>null</code>)
* @param file the workspace file underlying the element (not <code>null</code>)
* @param owner the working copy owner, or <code>null</code> if the primary
* owner should be used
*/
public CompilationUnit(PackageFragment parent, IFile file,
WorkingCopyOwner owner)
{
super(parent, file.getName());
if (!file.getParent().equals(parent.getResource()))
throw new IllegalArgumentException();
if (!"java".equals(file.getFileExtension())) //$NON-NLS-1$
throw new IllegalArgumentException();
this.file = file;
if (owner == null)
owner = PRIMARY_OWNER;
this.owner = owner;
}
@Override
public PackageFragment getParent()
{
return (PackageFragment)getParent_();
}
@Override
public IFile getFile()
{
return getFile_();
}
@Override
public ImportDeclaration getImport(String name)
{
return getImportContainer().getImport(name);
}
@Override
public ImportContainer getImportContainer()
{
return new ImportContainer(this);
}
@Override
public IImportDeclaration[] getImports() throws CoreException
{
IImportContainer container = getImportContainer();
if (container.exists())
return container.getImports();
return NO_IMPORTS;
}
@Override
public PackageDeclaration getPackageDeclaration(String name)
{
return new PackageDeclaration(this, name);
}
@Override
public IPackageDeclaration[] getPackageDeclarations() throws CoreException
{
return getChildrenOfType(IPackageDeclaration.class);
}
@Override
public Type getType(String name)
{
return new Type(this, name);
}
@Override
public IType[] getTypes() throws CoreException
{
return getChildrenOfType(IType.class);
}
@Override
public IJavaSourceElement getElementAt(int position, ISnapshot base)
throws CoreException
{
return (IJavaSourceElement)getSourceElementAt_(position, of(
BASE_SNAPSHOT, base), null);
}
@Override
public boolean isWorkingCopy()
{
return isWorkingCopy_();
}
private static final Property<AstHolder> AST_HOLDER = Property.get(
CompilationUnit.class.getName() + ".astHolder", AstHolder.class); //$NON-NLS-1$
@Override
public org.eclipse.jdt.core.dom.CompilationUnit reconcile(int astLevel,
int reconcileFlags, IProgressMonitor monitor) throws CoreException
{
Context context = new Context();
if (astLevel != NO_AST)
{
context.bind(AST_HOLDER).to(new AstHolder());
context.bind(AST_LEVEL).to(astLevel);
context.bind(STRUCTURAL_AST).to(false);
context.bind(RESOLVE_BINDINGS).to(true);
context.bind(STATEMENTS_RECOVERY).to((reconcileFlags
& ENABLE_STATEMENTS_RECOVERY) != 0);
context.bind(BINDINGS_RECOVERY).to((reconcileFlags
& ENABLE_BINDINGS_RECOVERY) != 0);
context.bind(IGNORE_METHOD_BODIES).to((reconcileFlags
& ICompilationUnit.IGNORE_METHOD_BODIES) != 0);
}
context.bind(FORCE_RECONCILING).to((reconcileFlags
& FORCE_PROBLEM_DETECTION) != 0);
reconcile_(context, monitor);
if (astLevel != NO_AST)
return context.get(AST_HOLDER).ast;
return null;
}
@Override
public IBuffer getBuffer() throws CoreException
{
return getBuffer_(EMPTY_CONTEXT, null);
}
@Override
public boolean equals(Object obj)
{
if (!(obj instanceof CompilationUnit))
return false;
CompilationUnit other = (CompilationUnit)obj;
return owner.equals(other.owner) && super.equals(obj);
}
@Override
public IResource getResource_()
{
return file;
}
/**
* Returns the underlying {@link IFile}. This is a handle-only method.
*
* @return the underlying <code>IFile</code> (never <code>null</code>)
*/
@Override
public IFile getFile_()
{
return file;
}
@Override
public void validateExistence_(IContext context) throws CoreException
{
ISourceFileImplSupport.super.validateExistence_(context);
IStatus status = validateCompilationUnitName();
if (status.getSeverity() == IStatus.ERROR)
throw new CoreException(status);
}
IStatus validateCompilationUnitName()
{
JavaProject javaProject = getAncestorOfType(JavaProject.class);
String sourceLevel = javaProject.getOption(JavaCore.COMPILER_SOURCE,
true);
String complianceLevel = javaProject.getOption(
JavaCore.COMPILER_COMPLIANCE, true);
return JavaConventions.validateCompilationUnitName(getElementName(),
sourceLevel, complianceLevel);
}
static final Property<Integer> AST_LEVEL = Property.get(
CompilationUnit.class.getName() + ".astLevel", //$NON-NLS-1$
Integer.class).withDefault(AST.JLS11);
static final Property<Boolean> STRUCTURAL_AST = Property.get(
CompilationUnit.class.getName() + ".structuralAst", //$NON-NLS-1$
Boolean.class).withDefault(true);
static final Property<Integer> FOCAL_POSITION = Property.get(
CompilationUnit.class.getName() + ".focalPosition", Integer.class); //$NON-NLS-1$
static final Property<Boolean> RESOLVE_BINDINGS = Property.get(
CompilationUnit.class.getName() + ".resolveBindings", //$NON-NLS-1$
Boolean.class).withDefault(false);
static final Property<Boolean> STATEMENTS_RECOVERY = Property.get(
CompilationUnit.class.getName() + ".statementsRecovery", //$NON-NLS-1$
Boolean.class).withDefault(false);
static final Property<Boolean> BINDINGS_RECOVERY = Property.get(
CompilationUnit.class.getName() + ".bindingsRecovery", //$NON-NLS-1$
Boolean.class).withDefault(false);
static final Property<Boolean> IGNORE_METHOD_BODIES = Property.get(
CompilationUnit.class.getName() + ".ignoreMethodBodies", //$NON-NLS-1$
Boolean.class).withDefault(false);
org.eclipse.jdt.core.dom.CompilationUnit createAst(String source,
IContext context, IProgressMonitor monitor) throws CoreException
{
ASTParser parser = ASTParser.newParser(context.getOrDefault(AST_LEVEL));
parser.setSource(source.toCharArray());
parser.setUnitName(getPath().toString());
parser.setProject(JavaCore.create(getResource().getProject()));
if (context.containsKey(FOCAL_POSITION))
parser.setFocalPosition(context.get(FOCAL_POSITION));
else if (context.getOrDefault(STRUCTURAL_AST))
parser.setFocalPosition(0);
parser.setResolveBindings(context.getOrDefault(RESOLVE_BINDINGS));
parser.setStatementsRecovery(context.getOrDefault(STATEMENTS_RECOVERY));
parser.setBindingsRecovery(context.getOrDefault(BINDINGS_RECOVERY));
parser.setIgnoreMethodBodies(context.getOrDefault(
IGNORE_METHOD_BODIES));
return (org.eclipse.jdt.core.dom.CompilationUnit)parser.createAST(
monitor);
}
@Override
public void buildSourceStructure_(IContext context,
IProgressMonitor monitor) throws CoreException
{
org.eclipse.jdt.core.dom.CompilationUnit cu =
(org.eclipse.jdt.core.dom.CompilationUnit)context.get(SOURCE_AST);
if (cu == null)
cu = createAst(context.get(SOURCE_CONTENTS), context, monitor);
CompilatonUnitStructureBuilder builder =
new CompilatonUnitStructureBuilder(context.get(NEW_ELEMENTS));
builder.buildStructure(this, cu);
}
@Override
public IContext newWorkingCopyContext_(IContext context)
{
return with(context.getOrDefault(WORKING_COPY_CONTEXT), of(
IProblemRequestor.class, context.get(IProblemRequestor.class)));
}
@Override
public ReconcileOperation getReconcileOperation_()
{
return new CuReconcileOperation();
}
@Override
protected char getHandleMementoDelimiter()
{
return JEM_COMPILATIONUNIT;
}
@Override
protected JavaElement getHandleFromMemento(String token,
MementoTokenizer memento)
{
if (token == MementoTokenizer.IMPORTDECLARATION)
{
return getImportContainer().getHandleFromMemento(memento);
}
else if (token == MementoTokenizer.PACKAGEDECLARATION
|| token == MementoTokenizer.TYPE)
{
String name = ""; //$NON-NLS-1$
String nextToken = null;
if (memento.hasMoreTokens())
{
nextToken = memento.nextToken();
if (!MementoTokenizer.isDelimeter(nextToken))
{
name = nextToken;
nextToken = null;
}
}
JavaElement element;
if (token == MementoTokenizer.PACKAGEDECLARATION)
element = getPackageDeclaration(name);
else if (token == MementoTokenizer.TYPE)
element = getType(name);
else
throw new AssertionError();
if (nextToken == null)
return element.getHandleFromMemento(memento);
else
return element.getHandleFromMemento(token, memento);
}
return null;
}
private class CuReconcileOperation
extends NotifyingReconcileOperation
{
CuReconcileOperation()
{
super(CompilationUnit.this);
}
@Override
protected void reconcile(IContext context, IProgressMonitor monitor)
throws CoreException
{
org.eclipse.jdt.core.dom.CompilationUnit cu =
(org.eclipse.jdt.core.dom.CompilationUnit)context.get(
SOURCE_AST);
if (cu == null)
{
cu = createAst(context.get(SOURCE_CONTENTS), context, monitor);
context = with(of(SOURCE_AST, cu), context);
}
super.reconcile(context, monitor);
reportProblems(cu.getProblems());
AstHolder astHolder = context.get(AST_HOLDER);
if (astHolder != null)
astHolder.ast = cu;
}
private void reportProblems(IProblem[] problems)
{
if (problems == null || problems.length == 0)
return;
reportProblems(getWorkingCopyContext_().get(
IProblemRequestor.class), problems);
}
private void reportProblems(IProblemRequestor requestor,
IProblem[] problems)
{
if (requestor == null || !requestor.isActive())
return;
try
{
requestor.beginReporting();
for (IProblem problem : problems)
{
requestor.acceptProblem(problem);
}
}
finally
{
requestor.endReporting();
}
}
}
private static class AstHolder
{
org.eclipse.jdt.core.dom.CompilationUnit ast;
}
}