| /********************************************************************** |
| * This file is part of "Object Teams Development Tooling"-Software |
| * |
| * Copyright 2006, 2007 Technical University Berlin, Germany 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 |
| * |
| * Please visit http://www.eclipse.org/objectteams for updates and contact. |
| * |
| * Contributors: |
| * Technical University Berlin - Initial API and implementation |
| * IBM Corporation - copies of individual methods from bound base classes. |
| **********************************************************************/ |
| package org.eclipse.objectteams.otdt.internal.corext; |
| |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.jdt.core.Flags; |
| import org.eclipse.jdt.core.IField; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.dom.AST; |
| import org.eclipse.jdt.core.dom.ASTNode; |
| import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; |
| import org.eclipse.jdt.core.dom.CompilationUnit; |
| import org.eclipse.jdt.core.dom.FieldAccessSpec; |
| import org.eclipse.jdt.core.dom.IBinding; |
| import org.eclipse.jdt.core.dom.ITypeBinding; |
| import org.eclipse.jdt.core.dom.MethodSpec; |
| import org.eclipse.jdt.core.dom.Modifier; |
| import org.eclipse.jdt.core.dom.SimpleName; |
| import org.eclipse.jdt.core.dom.Type; |
| import org.eclipse.jdt.core.dom.TypeLiteral; |
| import org.eclipse.jdt.core.search.TypeNameMatch; |
| import org.eclipse.jdt.internal.corext.dom.ASTNodes; |
| import org.eclipse.jdt.internal.corext.dom.Bindings; |
| import org.eclipse.objectteams.otdt.core.compiler.IOTConstants; |
| |
| // org.eclipse.jdt.core: |
| import base org.eclipse.jdt.core.dom.ASTVisitor; |
| //org.eclipse.jdt.ui: |
| import base org.eclipse.jdt.internal.corext.codemanipulation.GetterSetterUtil; |
| // org.eclipse.jdt.core.manipulation: |
| import base org.eclipse.jdt.core.manipulation.ImportReferencesCollector; |
| import base org.eclipse.jdt.core.manipulation.OrganizeImportsOperation; |
| import base org.eclipse.jdt.core.manipulation.OrganizeImportsOperation.TypeReferenceProcessor; |
| import base org.eclipse.jdt.internal.corext.dom.ScopeAnalyzer; |
| |
| |
| /** |
| * @author stephan |
| */ |
| @SuppressWarnings({"restriction","decapsulation"}) |
| public team class CodeManipulationAdaptor |
| { |
| protected class TypeReferenceProcessor playedBy TypeReferenceProcessor |
| { |
| ScopeAnalyzer fAnalyzer() -> get ScopeAnalyzer fAnalyzer; |
| |
| needsImport <- replace needsImport; |
| @SuppressWarnings({ "basecall", "unchecked" }) |
| callin boolean needsImport(ITypeBinding typeBinding, SimpleName ref) |
| { |
| // OT_COPY_PASTE: |
| |
| //{ObjectTeams: check if we need to apply OT-specific strategy: |
| boolean isTeam = false; |
| // cache: |
| ScopeAnalyzer analyzer = fAnalyzer(); |
| List<AbstractTypeDeclaration> types = analyzer.fRoot().types(); |
| for (AbstractTypeDeclaration type : types) { |
| if (Modifier.isTeam(type.getModifiers())) { |
| isTeam = true; |
| break; |
| } |
| } |
| if (!isTeam) // simply use the original version: |
| return base.needsImport(typeBinding, ref); |
| // SH} |
| |
| if (!typeBinding.isTopLevel() && !typeBinding.isMember() || typeBinding.isRecovered()) { |
| return false; // no imports for anonymous, local, primitive types or parameters types |
| } |
| /*{ObjectTeams disable visibility issues for OT: |
| /* orig |
| int modifiers= typeBinding.getModifiers(); |
| if (Modifier.isPrivate(modifiers)) { |
| return false; // imports for privates are not required |
| } |
| :giro */ |
| ITypeBinding currTypeBinding= Bindings.getBindingOfParentType(ref); |
| if (currTypeBinding == null) { |
| if (ASTNodes.getParent(ref, ASTNode.PACKAGE_DECLARATION) != null) { |
| return true; // reference in package-info.java |
| } |
| return false; // not in a type |
| } |
| /* orig: |
| if (!Modifier.isPublic(modifiers)) { |
| if (!currTypeBinding.getPackage().getName().equals(typeBinding.getPackage().getName())) { |
| return false; // not visible |
| } |
| } |
| :giro */ |
| // SH} |
| |
| ASTNode parent= ref.getParent(); |
| while (parent instanceof Type) { |
| parent= parent.getParent(); |
| } |
| if (parent instanceof AbstractTypeDeclaration && parent.getParent() instanceof CompilationUnit) { |
| return true; |
| } |
| |
| if (typeBinding.isMember()) { |
| //{ObjectTeams: never import a role |
| if (typeBinding.isRole()) |
| return false; |
| // SH} |
| // use cached instance: |
| if (analyzer.isDeclaredInScope(typeBinding, ref, analyzer.TYPES | analyzer.CHECK_VISIBILITY)) |
| return false; |
| } |
| return true; |
| } |
| |
| isOfKind <- replace isOfKind; |
| |
| @SuppressWarnings("basecall") |
| callin boolean isOfKind(TypeNameMatch curr) { |
| // a role type is NEVER a good guess for a missing import. |
| if (Flags.isRole(curr.getModifiers())) |
| return false; |
| return base.isOfKind(curr); |
| } |
| |
| } |
| |
| protected class ScopeAnalyzer playedBy ScopeAnalyzer |
| { |
| // expose constants as role fields: |
| protected int TYPES; |
| protected int CHECK_VISIBILITY; |
| |
| ScopeAnalyzer(ScopeAnalyzer b) { |
| TYPES = getTYPES(); |
| CHECK_VISIBILITY = getCHECK_VISIBILITY(); |
| } |
| |
| protected |
| boolean isDeclaredInScope(IBinding declaration, SimpleName selector, int flags) -> boolean isDeclaredInScope(IBinding declaration, SimpleName selector, int flags); |
| protected |
| CompilationUnit fRoot() -> get CompilationUnit fRoot; |
| // internal: |
| int getTYPES() -> get int TYPES; |
| int getCHECK_VISIBILITY() -> get int CHECK_VISIBILITY; |
| } |
| |
| /** |
| * Weave into class from org.eclipse.jdt.core |
| */ |
| protected class MethodSpecRecordingCoreVisitor playedBy ASTVisitor { |
| boolean inMethodSpec= false; |
| void setMethodSpec(boolean in) { |
| this.inMethodSpec= in; |
| } |
| void setMethodSpec(boolean in) <- after boolean visit(MethodSpec spec) |
| with { in <- true } |
| void setMethodSpec(boolean in) <- before void endVisit(MethodSpec spec) |
| with { in <- false} |
| void setMethodSpec(boolean in) <- after boolean visit(FieldAccessSpec spec) |
| with { in <- true } |
| void setMethodSpec(boolean in) <- before void endVisit(FieldAccessSpec spec) |
| with { in <- false} |
| } |
| /** |
| * Make sure that method specs never trigger static imports of their |
| * references methods/fields. |
| */ |
| protected class ImportReferencesCollector extends MethodSpecRecordingCoreVisitor playedBy ImportReferencesCollector { |
| |
| @SuppressWarnings("basecall") |
| callin void possibleStaticImportFound() { |
| // nop |
| } |
| possibleStaticImportFound <- replace possibleStaticImportFound |
| when (this.inMethodSpec); |
| } |
| |
| /** |
| * This role advises the OrganizeImportsOperation to create base imports for role files, too. |
| */ |
| protected class OrganizeImports playedBy OrganizeImportsOperation { |
| |
| void collectRoFiBaseReferences(CompilationUnit astRoot, List<SimpleName> typeReferences) |
| <- after boolean collectReferences(CompilationUnit astRoot, List<SimpleName>typeReferences, List<SimpleName> staticReferences, Set<String> oldSingleImports, Set<String> oldDemandImports) |
| base when (result); |
| |
| void collectRoFiBaseReferences(CompilationUnit astRoot, List<SimpleName> typeReferences) { |
| for (String baseName : RoleFileAdaptor.getRoFiBaseClassNames(astRoot)) |
| typeReferences.add(newTypeRef(astRoot, baseName)); |
| } |
| |
| SimpleName newTypeRef(CompilationUnit astRoot, String baseName) { |
| AST ast = astRoot.getAST(); |
| SimpleName ref = ast.newSimpleName(baseName); |
| // wrap in a type literal so that downstream will reckognize this as a type reference |
| TypeLiteral wrapper = ast.newTypeLiteral(); |
| wrapper.setType(ast.newSimpleType(ref)); |
| return ref; |
| } |
| } |
| |
| /** |
| * This role ensures nobody tries to create getters/setters for internal fields like _OT$base. |
| * This is done by generating an unexpected name which will be filtered out |
| * e.g. by GetterSetterCompletionProposal#evaluateProposals(). |
| */ |
| protected class GetterSetterUtil playedBy GetterSetterUtil |
| { |
| // this name does NOT start with "get" as expected. |
| final static String NOT_A_VALID_NAME = "_OT$notValidName"; //$NON-NLS-1$ |
| |
| String getGetterSetterName(IField field) <- replace |
| String getGetterName(IField field, String[] excludedNames), |
| String getSetterName(IField field, String[] excludedNames); |
| |
| @SuppressWarnings("basecall") |
| static callin String getGetterSetterName(IField field) throws JavaModelException { |
| if (field.getElementName().startsWith(IOTConstants.OT_DOLLAR)) { |
| return NOT_A_VALID_NAME; |
| } |
| return base.getGetterSetterName(field); |
| } |
| } |
| } |