| /******************************************************************************* |
| * 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; |
| } |
| } |