| /********************************************************************** |
| * This file is part of "Object Teams Development Tooling"-Software |
| * |
| * Copyright 2003, 2006 Fraunhofer Gesellschaft, Munich, Germany, |
| * for its Fraunhofer Institute for Computer Architecture and Software |
| * Technology (FIRST), Berlin, Germany and Technical University Berlin, |
| * Germany. |
| * |
| * 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 |
| * $Id: LiftingEnvironment.java 23416 2010-02-03 19:59:31Z stephan $ |
| * |
| * Please visit http://www.eclipse.org/objectteams for updates and contact. |
| * |
| * Contributors: |
| * Fraunhofer FIRST - Initial API and implementation |
| * Technical University Berlin - Initial API and implementation |
| **********************************************************************/ |
| package org.eclipse.objectteams.otdt.internal.core.compiler.lifting; |
| |
| import static org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.AccPublic; |
| import static org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.AccSynthetic; |
| import static org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.AccTransient; |
| import static org.eclipse.objectteams.otdt.core.compiler.IOTConstants.CACHE_PREFIX; |
| |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Vector; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.Annotation; |
| import org.eclipse.jdt.internal.compiler.ast.EqualExpression; |
| import org.eclipse.jdt.internal.compiler.ast.Expression; |
| import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.OperatorIds; |
| import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; |
| import org.eclipse.jdt.internal.compiler.ast.Statement; |
| import org.eclipse.jdt.internal.compiler.ast.ThisReference; |
| import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.TypeReference; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.jdt.internal.compiler.impl.Constant; |
| import org.eclipse.jdt.internal.compiler.impl.CompilerOptions.WeavingScheme; |
| import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; |
| import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; |
| import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; |
| import org.eclipse.objectteams.otdt.core.compiler.IOTConstants; |
| import org.eclipse.objectteams.otdt.core.exceptions.InternalCompilerError; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.control.ITranslationStates; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.control.StateMemento; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.model.RoleModel; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.model.TeamModel; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.smap.SourcePosition; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.smap.StepOverSourcePosition; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.transformer.ReflectionGenerator; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.transformer.RoleMigrationImplementor; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.transformer.SerializationGenerator; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.transformer.StandardElementGenerator; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.transformer.TeamMethodGenerator; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstConverter; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstEdit; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstGenerator; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.util.TypeAnalyzer; |
| |
| /** |
| * This class stores context information, esp. the hierarchies of roles of a given |
| * team. This information is used for creating the lifting infrastructure and |
| * a few related things. |
| * It is also responsible for create the role caches and a method for their initialization. |
| * |
| * @author Markus Witte |
| * @version $Id: LiftingEnvironment.java 23416 2010-02-03 19:59:31Z stephan $ |
| */ |
| public class LiftingEnvironment |
| { |
| private ProblemReporter _problemReporter; |
| private TypeDeclaration _teamType; |
| |
| /* all roles of the current team. */ |
| private TreeNode[] _roles; |
| /* create one role cache for each of these: */ |
| private TreeNode[] _boundRootRoles; |
| |
| /* key: target role to lift to |
| * values: sub-roles that can be created by the liftTo method. */ |
| private HashMap<RoleModel,RoleModel[]> _caseObjects = new HashMap<RoleModel,RoleModel[]>(); |
| /** |
| * Setup a lifting environment by analyzing a team's role hierarchies. |
| * Stores the results of analysis in this lifting environment. |
| * @param teamType |
| */ |
| public LiftingEnvironment(TypeDeclaration teamType) |
| { |
| init(teamType); |
| } |
| public void init(TypeDeclaration teamType) |
| { |
| this._problemReporter = teamType.scope.problemReporter(); |
| this._teamType = teamType; |
| this._roles = getRoles(); |
| connectHierarchy(this._roles); |
| this._boundRootRoles = filterBoundRootNodes(this._roles); |
| } |
| |
| /** |
| * Analyze the role hierarchy and create: |
| * + RoleBaseBindingAttribute |
| * + _OT$base fields |
| * + liftto constructors (includes removal of default constructors) |
| * |
| * @param lateRole if set create infrastructure only for this late accepted role file. |
| */ |
| public void createRoleBaseLinkage(RoleModel lateRole, boolean needMethodBodies) |
| { |
| if(lateRole != null && lateRole.isSynthInterface()) |
| return; // no base field here. FIXME(SH): what about addRoleBaseBindingAttribute? |
| |
| TreeNode[] boundRoles = getBoundRoles(); |
| if ((boundRoles == null) || (boundRoles.length == 0)) |
| return; |
| |
| Lifting lifting = new Lifting(); |
| for (int i = 0; i < boundRoles.length; i++) |
| { |
| TreeNode currentNode = boundRoles[i]; |
| RoleModel currentRole = currentNode.getTreeObject(); |
| if ((lateRole != null) && (currentRole != lateRole)) |
| continue; // only process 'lateRole' |
| this._caseObjects.put(currentRole, createOneRoleBaseLinkage(lifting, currentNode, needMethodBodies)); |
| if (lateRole != null) |
| return; // after processing one role we're done |
| } |
| if (lateRole != null && !lateRole.hasBaseclassProblem()) // after base class prob don't expect too much. |
| throw new InternalCompilerError("late role not found in boundRoles: "+new String(lateRole.getBinding().internalName())); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Creates the following infrastructure for lifting: |
| * + role caches |
| * + liftto methods |
| * + reflection methods |
| * |
| * @param lateRole |
| * @param needMethodBodies |
| */ |
| public void createLiftingInfrastructure(RoleModel lateRole, boolean needMethodBodies, WeavingScheme weavingScheme) { |
| TreeNode[] boundRoles = getBoundRoles(); |
| if ((boundRoles == null) || (boundRoles.length == 0)) { |
| if (lateRole == null && needMethodBodies) |
| ReflectionGenerator.createRoleQueryMethods(this._teamType, weavingScheme); |
| return; |
| } |
| |
| if (lateRole == null) |
| TeamMethodGenerator.addFakedTeamRegistrationMethods(this._teamType.binding); |
| |
| Lifting lifting = new Lifting(); |
| // create lift methods only after all bound base classes have been seen |
| for (int i = 0; i < boundRoles.length; i++) |
| { |
| if (lateRole != null && boundRoles[i].getTreeObject() != lateRole) |
| continue; |
| RoleModel[] cases = this._caseObjects.get(boundRoles[i].getTreeObject()); |
| if (cases != null) |
| lifting.createLiftToMethod(this._teamType, boundRoles[i], cases); |
| } |
| // the the TreeNode corresponding to the current lateRole: |
| TreeNode node = null; |
| for (int i = 0; i < boundRoles.length; i++) |
| { |
| if (lateRole == boundRoles[i].getTreeObject()) { |
| node = boundRoles[i]; |
| break; // only process this one role |
| } |
| } |
| // Different strategies for early/late roles: |
| // 1. role caches |
| // 2. reflection methods |
| if (lateRole == null) { |
| generateRoleCaches(this._teamType); |
| // TODO(SH): split into decl and statements (see fillGeneratedMethods()). |
| if (needMethodBodies) |
| ReflectionGenerator.createRoleQueryMethods(this._teamType, weavingScheme); |
| } else { |
| // isTopBound? |
| if (node != null && node.getTopmostBoundParent(true) == node) { |
| generateRoleCache(this._teamType, lateRole); |
| } |
| } |
| } |
| |
| /** |
| * @param lifting |
| * @param node representing the role to generate for |
| * @return array of case objects for lift method creation |
| */ |
| private RoleModel[] createOneRoleBaseLinkage( |
| Lifting lifting, |
| TreeNode node, |
| boolean needMethodBodies) |
| { |
| RoleModel[] result = null; |
| RoleModel roleModel = node.getTreeObject(); |
| if (roleModel.hasBaseclassProblem()) |
| return null; |
| |
| TypeDeclaration roleType = roleModel.getAst(); |
| if (roleType != null && roleType.isRoleFile() && roleType.compilationUnit != null) |
| needMethodBodies = roleType.compilationUnit.parseMethodBodies; |
| |
| // before accessing base class check its presence: |
| if (roleModel.getBaseTypeBinding() == null && roleType == null) // stale binary |
| this._problemReporter.staleSubRole(roleModel.getBinding(), roleModel.getBinding().superclass()); // this aborts |
| |
| // create elements for the team: |
| // Attribute: |
| roleModel.getTeamModel().addRoleBaseBindingAttribute( |
| roleModel.getBinding().attributeName(), |
| roleModel.getBaseclassAttributename(false), // don't include anchor |
| roleModel.getBaseTypeBinding().isInterface()); |
| // TODO(SH): move to STATE_FULL_LIFTING: (?) |
| // Analysis may detect errors and prepares for liftmethod generation. |
| RoleHierarchieAnalyzer analyzer = |
| new RoleHierarchieAnalyzer(this._teamType, this._problemReporter); |
| if (!roleModel.getBinding().isHierarchyInconsistent()) |
| result = analyzer.analyze(node); |
| |
| // specific elements for the role only if not binary: |
| if (!roleModel.getBinding().isBinaryBinding()) { |
| if (roleType != null && !roleModel.getBinding().isSynthInterface()) |
| { |
| StandardElementGenerator.generatePlayedByElements( |
| roleType.getRoleModel(), |
| node.getBoundParent(false)); |
| lifting.createLiftToConstructorDeclaration(node, needMethodBodies); |
| AstEdit.removeDefaultConstructor(roleType); |
| RoleMigrationImplementor.checkAddMigrateToBaseMethod(roleType, node); |
| } |
| } else { |
| // mini version for binaries: |
| if (!roleModel.getBinding().isInterface()) |
| setBoundRootRole(node); // otherwise done in createLiftToConstructorDeclaration() |
| } |
| return result; |
| } |
| |
| private static void setBoundRootRole(TreeNode roleNode) { |
| // for determining the cache interfaces are allowed: |
| RoleModel boundRootRoleModel = roleNode.getTopmostBoundParent(true).getTreeObject(); |
| if (boundRootRoleModel != null) |
| roleNode.getTreeObject()._boundRootRole = boundRootRoleModel; |
| } |
| |
| /** |
| * @param boundRootRole |
| */ |
| public static char[] getCacheName(RoleModel boundRootRole) { |
| if (boundRootRole == null) |
| return null; |
| return CharOperation.concat(CACHE_PREFIX, boundRootRole.getBinding().sourceName()); |
| } |
| |
| /** |
| * For each bound root role one lifting cache is generated, |
| * unless the cache is already inherited from the super team. |
| * @param teamType |
| */ |
| private void generateRoleCaches(TypeDeclaration teamType) { |
| if (this._boundRootRoles == null || this._boundRootRoles.length == 0) |
| return; |
| for (int i=0; i<this._boundRootRoles.length; i++){ |
| generateRoleCache(teamType, this._boundRootRoles[i].getTreeObject()); |
| } |
| generateInitCaches(teamType); |
| } |
| |
| /** |
| * Generate a role cache regarding 'boundRootRole' and record it in the team model. |
| * @param teamDecl |
| * @param boundRootRole |
| */ |
| private static void generateRoleCache (TypeDeclaration teamDecl, RoleModel boundRootRole) |
| { |
| char[] cacheName = getCacheName(boundRootRole); |
| FieldBinding foundField = TypeAnalyzer.findField( |
| teamDecl.binding, cacheName, /*static*/false, /*outer*/false, |
| ITranslationStates.STATE_FULL_LIFTING); // generated in this state |
| |
| if (foundField != null) |
| return; |
| int sStart, sEnd; |
| TypeDeclaration rootAst = boundRootRole.getAst(); |
| if (rootAst != null) { |
| sStart = rootAst.sourceStart; |
| sEnd = rootAst.sourceEnd; |
| } else { |
| sStart = teamDecl.sourceStart; |
| sEnd = teamDecl.sourceEnd; |
| } |
| boolean usingRawType = boundRootRole.getBaseTypeBinding().isParameterizedType(); |
| AstGenerator gen = new AstGenerator(sStart, sEnd); |
| if (usingRawType) |
| gen.shiftPosition(); // introduce a minimal offset (cf. also gen2 in ReflectionGenerator#createRoleQueryMethods) |
| |
| QualifiedTypeReference fieldTypeRef; |
| fieldTypeRef = gen.getCacheTypeReference(teamDecl.scope, boundRootRole); |
| |
| // Note (SH): caches are not initialized directly, but via method _OT$initCaches, |
| // which in turn is invoked as initialization of synthetic field _OT$cacheInitTrigger. |
| // This way, further initialization calls can easily be added elsewhere, |
| // like in team-ctors which add arg-lifting into the byte code. |
| |
| // caches are not final for the sake of initCaches-methods. |
| FieldDeclaration field = gen.field( |
| (AccPublic|AccSynthetic|AccTransient), fieldTypeRef, cacheName, /*alloc*/null); |
| // Note (SH): if the cache would be generated with lesser access rights |
| // (e.g., protected), we would need synthetic access methods, when accessing |
| // across packages, and all accesses would have to be redirected. |
| // Somehow, this doesn't happen automagically. |
| // But by declaring the caches public the issue is completely avoided, |
| // hoping, that no-one maliciously accesses the cache. |
| |
| // if base is generic, we refer to it via the raw type, suppress that warning: |
| // if (usingRawType) { |
| // field.annotations = new Annotation[] { |
| // gen.singleStringsMemberAnnotation(TypeConstants.JAVA_LANG_SUPPRESSWARNINGS, new char[][]{"rawtypes".toCharArray()}) //$NON-NLS-1$ |
| // }; |
| // } |
| |
| AstEdit.addField(teamDecl, field, true, false/*typeProblem*/, false); |
| teamDecl.getTeamModel().addCache(field); |
| } |
| |
| /** |
| * Create a method that initializes all caches introduced in a given team. |
| * Only create method declaration, no statements yet. |
| * (see fillGeneratedMethods()). |
| * |
| * @param teamType the type to add the method to. |
| * @return the _OT$initCaches method |
| */ |
| private static MethodDeclaration generateInitCaches(TypeDeclaration teamType) |
| { |
| MethodDeclaration initMethod = |
| AstConverter.findAndAdjustCopiedMethod( |
| teamType, IOTConstants.OT_INIT_CACHES, null); |
| if (initMethod == null) |
| { |
| AstGenerator gen = new AstGenerator(teamType.sourceStart, teamType.sourceEnd); |
| gen.shiftPosition(); // don't let @SuppressWarnings interfere with non-generated code. |
| |
| initMethod = gen.method( |
| teamType.compilationResult, |
| ClassFileConstants.AccPrivate, // no overriding!! |
| TypeBinding.BOOLEAN, |
| IOTConstants.OT_INIT_CACHES, |
| null); |
| initMethod.statements = new Statement[0]; // to be filled by fillInitCaches() |
| initMethod.annotations = new Annotation[] { // in case base type is a raw type |
| gen.singleStringsMemberAnnotation(TypeConstants.JAVA_LANG_SUPPRESSWARNINGS, new char[][]{"all".toCharArray()}) //$NON-NLS-1$ |
| }; |
| AstEdit.addMethod(teamType, initMethod); |
| initMethod.modifiers |= ExtraCompilerModifiers.AccLocallyUsed; // prevent 'unused' warning |
| |
| if (teamType.isRole()) { |
| TypeDeclaration ifcPart = teamType.getRoleModel().getInterfaceAst(); |
| if (ifcPart != teamType) { |
| AbstractMethodDeclaration methodIfcPart = |
| TypeAnalyzer.findMethodDecl(ifcPart, IOTConstants.OT_INIT_CACHES, 0); |
| if (methodIfcPart == null) { |
| methodIfcPart = AstConverter.genRoleIfcMethod( |
| teamType.enclosingType, initMethod); |
| AstEdit.addMethod(ifcPart, methodIfcPart); |
| } |
| } |
| } |
| |
| // Serialization: generate restore methods to initialize caches/register roles: |
| SerializationGenerator.generateRestoreMethods(teamType, gen); |
| } |
| |
| return initMethod; |
| } |
| |
| /** |
| * TODO(SH): use IStatementsGenerator instead? |
| * After late roles are in place create the statements for some generated methods. |
| * @param teamType |
| */ |
| public static void fillGeneratedMethods(TypeDeclaration teamType) { |
| fillInitCaches(teamType, teamType.getTeamModel().caches); |
| } |
| private static void fillInitCaches(TypeDeclaration teamType, FieldDeclaration[] caches) |
| { |
| /* |
| * generate: |
| * { |
| * // for each cache declared in this team: |
| * if (_OT$cache<x> == null) { |
| * _OT$cache<c> = new WeakHashMap<Bx,Rx>(); |
| * } |
| * // Note: no super call, super team's ctor is already responsible for invoking its (private) initCaches |
| * } |
| */ |
| AbstractMethodDeclaration initMethod = TypeAnalyzer.findMethodDecl(teamType, IOTConstants.OT_INIT_CACHES, 0); |
| if (initMethod == null) { |
| // not yet generated? (maybe no bound roles/caches at that time). |
| if (teamType.getTeamModel().caches.length == 0) |
| return; |
| initMethod = generateInitCaches(teamType); |
| } |
| AstGenerator gen = new AstGenerator(initMethod); // re-use position |
| Statement[] statements = new Statement[caches.length+1]; |
| for (int i = 0; i < caches.length; i++) { |
| // FIXME(SH): unclear if needed after allowing generated qualified role type referneces: |
| TypeReference cacheTypeRef = caches[i].type; // robustness, but with wrong source position |
| if (caches[i].type.resolvedType instanceof ParameterizedTypeBinding) { |
| // reconstruct a type reference from the resolved cache type |
| ParameterizedTypeBinding oldBinding = (ParameterizedTypeBinding)cacheTypeRef.resolvedType; |
| if (oldBinding.arguments.length == 2) { |
| ReferenceBinding roleBinding = (ReferenceBinding)oldBinding.arguments[1]; |
| // respect different status for base/role types (scope, decapsulation). |
| cacheTypeRef = gen.getCacheTypeReference(teamType.scope, roleBinding.roleModel); |
| } |
| } |
| statements[i] = gen.ifStatement( |
| new EqualExpression( |
| gen.singleNameReference(caches[i].name), |
| gen.nullLiteral(), |
| OperatorIds.EQUAL_EQUAL), |
| gen.block(new Statement[] { |
| gen.assignment( |
| gen.singleNameReference(caches[i].name), |
| gen.allocation(cacheTypeRef, new Expression[0])) |
| })); |
| |
| } |
| statements[caches.length] = gen.returnStatement(gen.booleanLiteral(true)); |
| initMethod.setStatements(statements); |
| // Serialization: |
| SerializationGenerator.fillRestoreRole(teamType, caches); |
| |
| // ===== also add the field that triggers invocation of this method from all constructors: ===== |
| |
| //save source positions from AstGenerator (ike) |
| SourcePosition savePos = gen.getSourcePosition(); |
| try { |
| //set STEP_OVER source positions (ike) |
| gen.setSourcePosition(new StepOverSourcePosition()); |
| |
| ThisReference thisReference = gen.thisReference(); |
| thisReference.resolvedType = teamType.binding.getRealClass(); // avoid wrapping of nested team |
| thisReference.constant = Constant.NotAConstant; |
| |
| FieldDeclaration trigger = gen.field( |
| ClassFileConstants.AccPrivate | ClassFileConstants.AccSynthetic, |
| gen.singleTypeReference("boolean".toCharArray()), //$NON-NLS-1$ |
| IOTConstants.CACHE_INIT_TRIGGERER, |
| gen.messageSend(thisReference, IOTConstants.OT_INIT_CACHES, new Expression[0])); |
| AstEdit.addField(teamType, trigger, true, false/*typeProblem*/, true/*addToFront*/); // ensure this field's init is generated first into the constructor |
| trigger.binding.modifiers |= ExtraCompilerModifiers.AccLocallyUsed; // prevent 'unused' warning; |
| |
| // resolve them both: |
| if (StateMemento.hasMethodResolveStarted(teamType.binding)) { |
| initMethod.resolve(teamType.scope); |
| trigger.resolve(teamType.initializerScope); |
| } |
| |
| } finally { |
| //restore source postions (ike) |
| gen.setSourcePosition(savePos); |
| } |
| } |
| |
| // ==== ANALYSING THE ROLE HIERARCHY ==== |
| |
| /** |
| * Get all bound roles of this team. |
| * @return array > 0 or null; |
| */ |
| private TreeNode[] getBoundRoles() |
| { |
| TreeNode[] allRoles = this._roles; |
| if ((allRoles == null) || (allRoles.length == 0)) |
| { |
| return null; |
| } |
| Vector<TreeNode> boundRoles = new Vector<TreeNode>(0, 1); |
| for (int i = 0; i < allRoles.length; i++) |
| { |
| TreeNode node = allRoles[i]; |
| RoleModel roleModel = node.getTreeObject(); |
| if ( roleModel.isBound() |
| && !roleModel.hasBaseclassProblem()) |
| { |
| boundRoles.add(node); |
| } |
| } |
| if (boundRoles.size() == 0) |
| { |
| return null; |
| } |
| return boundRoles.toArray(new TreeNode[boundRoles.size()]); |
| } |
| |
| /** |
| * Linking TreeNodes for all roles: add children to parents in roles. |
| * Finds all pairs of parent-child. |
| * @param roles |
| */ |
| private void connectHierarchy(TreeNode[] roles) |
| { |
| TeamModel teamModel = this._teamType.getTeamModel(); |
| HashSet<ReferenceBinding> baseTypes = new HashSet<ReferenceBinding>(); |
| for (int i = 0; i < roles.length; i++) { |
| TreeNode parentNode = roles[i]; |
| // TODO(SH): potential for optimization: start with "j=_numChildrenSeen" |
| for (int j = 0; j < roles.length; j++) { |
| if (i == j) continue; |
| TreeNode childNode = roles[j]; |
| if ((parentNode.getTreeObject()).isSuperTypeOf( |
| childNode.getTreeObject())) |
| { |
| // connect TreeNodes: |
| parentNode.add(childNode); |
| // build BoundClassesHierarchy (role side): |
| teamModel.addBoundClassLink( |
| childNode.getTreeObject().getBinding(), |
| parentNode.getTreeObject().getBinding()); |
| } |
| } |
| // remember base types: |
| ReferenceBinding parentBase = parentNode.getTreeObject().getBaseTypeBinding(); |
| if (parentBase != null) |
| baseTypes.add(parentBase); |
| } |
| // build BoundClassesHierarchy (base side): |
| for(ReferenceBinding childBase: baseTypes) { |
| ReferenceBinding currentBase = childBase.superclass(); |
| while (currentBase != null) { |
| if (baseTypes.contains(currentBase)) { |
| teamModel.addBoundClassLink(childBase, currentBase); |
| break; |
| } |
| currentBase = currentBase.superclass(); |
| } |
| } |
| } |
| |
| |
| /** |
| * Collect all direct roles from a given team only omitting |
| * synthetic role interfaces. |
| * @return non-null array. |
| */ |
| private TreeNode[] getRoles() |
| { |
| ReferenceBinding[] roles = this._teamType.binding.memberTypes; |
| TreeNode[] treeNodes = new TreeNode[roles.length]; |
| int count = 0; |
| for (int j = 0; j < roles.length; j++) |
| { |
| ReferenceBinding role = roles[j]; |
| if (role.isEnum()) |
| continue; |
| if (!role.roleModel.isSynthInterface()) |
| { |
| RoleModel roleModel = role.roleModel; |
| TreeNode treeNode = new TreeNode(roleModel); |
| treeNodes[count++] = treeNode; |
| } |
| } |
| TreeNode[] treeRoles = new TreeNode[count]; |
| System.arraycopy(treeNodes, 0, treeRoles, 0, count); |
| return treeRoles; |
| } |
| |
| /** |
| * Extract from a set of roles only those that are bound and have no |
| * bound parent (super-type). |
| * @param treeNodes |
| * @return bound root roles. |
| */ |
| private static TreeNode[] filterBoundRootNodes(TreeNode[] treeNodes) |
| { |
| TreeNode[] liftingTypeTrees = new TreeNode[treeNodes.length]; |
| int count = 0; |
| for (int i = 0; i < treeNodes.length; i++) |
| { |
| TreeNode node = treeNodes[i]; |
| if ( node.getTreeObject().isBound() |
| && !node.hasBoundParent(true) |
| && !node.getTreeObject().hasBaseclassProblem()) |
| { |
| liftingTypeTrees[count++] = node; |
| } |
| } |
| TreeNode[] rootNodeTrees = new TreeNode[count]; |
| System.arraycopy(liftingTypeTrees, 0, rootNodeTrees, 0, count); |
| return rootNodeTrees; |
| } |
| |
| @Override |
| public String toString() |
| { |
| String str = ""; //$NON-NLS-1$ |
| if (this._boundRootRoles == null) |
| { |
| return str; |
| } |
| str += "RootRolesHierarchy:\n"; //$NON-NLS-1$ |
| for (int i = 0; i < this._boundRootRoles.length; i++) |
| { |
| TreeNode roleNode = this._boundRootRoles[i]; |
| str += "RootRole:\n"; //$NON-NLS-1$ |
| str += roleNode.toString(1); |
| } |
| str += "\n"; //$NON-NLS-1$ |
| return str; |
| } |
| } |