/******************************************************************************* * Copyright (c) 2009 IBM Corporation and others. * 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 * * Contributors: * IBM Corporation - initial API and implementation * Zend Technologies *******************************************************************************/ package org.eclipse.php.internal.core.typeinference; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.dltk.ast.Modifiers; import org.eclipse.dltk.ast.declarations.ModuleDeclaration; import org.eclipse.dltk.ast.expressions.Expression; import org.eclipse.dltk.ast.references.SimpleReference; import org.eclipse.dltk.ast.references.TypeReference; import org.eclipse.dltk.core.*; import org.eclipse.dltk.core.index2.search.ISearchEngine.MatchRule; import org.eclipse.dltk.core.search.IDLTKSearchScope; import org.eclipse.dltk.core.search.SearchEngine; import org.eclipse.dltk.internal.core.ModelElement; import org.eclipse.dltk.internal.core.SourceField; import org.eclipse.dltk.internal.core.SourceMethod; import org.eclipse.php.core.compiler.PHPFlags; import org.eclipse.php.core.compiler.ast.nodes.*; import org.eclipse.php.internal.core.compiler.ast.visitor.TraitUseStatementVisitor; import org.eclipse.php.internal.core.model.PHPModelAccess; /** * TODO namespace,precedence and alias * * @author zhaozw * */ public class TraitUtils { public static UseTrait parse(IType type) { final UseTrait useTrait = new UseTrait(); final ISourceModule sourceModule = type.getSourceModule(); try { final ISourceRange sourceRange = type.getSourceRange(); ModuleDeclaration moduleDeclaration = SourceParserUtil.getModuleDeclaration(sourceModule); moduleDeclaration.traverse(new TraitUseStatementVisitor() { @Override public boolean visit(TraitUseStatement s) throws Exception { if (s.sourceStart() > sourceRange.getOffset() && s.sourceEnd() < sourceRange.getOffset() + sourceRange.getLength()) { parse(sourceModule, sourceRange.getOffset(), s, useTrait); } return false; } }); } catch (ModelException e1) { } catch (Exception e) { } return useTrait; } public static UseTrait parse(ISourceModule sourceModule, int offset, TraitUseStatement statement, UseTrait useTrait) { if (useTrait == null) { useTrait = new UseTrait(); } for (TypeReference typeReference : statement.getTraitList()) { String name = typeReference.getName(); if (typeReference instanceof FullyQualifiedReference) { FullyQualifiedReference reference = (FullyQualifiedReference) typeReference; if (reference.getNamespace() != null) { name = reference.getNamespace().getName() + NamespaceReference.NAMESPACE_SEPARATOR + name; } } useTrait.getTraits().add(PHPModelUtils.getFullName(name, sourceModule, offset)); } for (TraitStatement traitStatement : statement.getTsList()) { if (traitStatement instanceof TraitAliasStatement) { TraitAliasStatement new_name = (TraitAliasStatement) traitStatement; Expression traitMethod = new_name.getAlias().getTraitMethod(); if (traitMethod instanceof SimpleReference) { SimpleReference simpleReference = (SimpleReference) traitMethod; TraitAliasObject ta = new TraitAliasObject(); // ta.traitName = PHPModelUtils.getFullName( // simpleReference.getName(), sourceModule, offset); ta.traitMethodName = simpleReference.getName(); ta.newMethodVisibility = new_name.getAlias().getModifier(); if (new_name.getAlias().getMethodName() != null) { ta.newMethodName = new_name.getAlias().getMethodName().getName(); } useTrait.getTraitAliases().add(ta); List<TraitAliasObject> traitAliases = useTrait.getAliasMap().get(ta.traitMethodName); if (traitAliases == null) { traitAliases = new ArrayList<TraitAliasObject>(); useTrait.getAliasMap().put(ta.traitMethodName, traitAliases); } traitAliases.add(ta); // useTrait.getAliasMap().put(ta.traitMethodName, ta); } else if (traitMethod instanceof FullyQualifiedTraitMethodReference) { FullyQualifiedTraitMethodReference simpleReference = (FullyQualifiedTraitMethodReference) traitMethod; TraitAliasObject ta = new TraitAliasObject(); ta.traitName = PHPModelUtils.getFullName(simpleReference.getClassName().getName(), sourceModule, offset); ta.traitMethodName = simpleReference.getFunctionName(); ta.newMethodVisibility = new_name.getAlias().getModifier(); if (new_name.getAlias().getMethodName() != null) { ta.newMethodName = new_name.getAlias().getMethodName().getName(); } useTrait.getTraitAliases().add(ta); List<TraitAliasObject> traitAliases = useTrait.getAliasMap().get(ta.traitMethodName); if (traitAliases == null) { traitAliases = new ArrayList<TraitAliasObject>(); useTrait.getAliasMap().put(ta.traitMethodName, traitAliases); } traitAliases.add(ta); // useTrait.getAliasMap().put(ta.traitMethodName, ta); } } else if (traitStatement instanceof TraitPrecedenceStatement) { TraitPrecedenceStatement new_name = (TraitPrecedenceStatement) traitStatement; TraitPrecedenceObject tpo = new TraitPrecedenceObject(); tpo.traitName = PHPModelUtils.getFullName( new_name.getPrecedence().getMethodReference().getClassName().getName(), sourceModule, offset); tpo.traitMethodName = new_name.getPrecedence().getMethodReference().getFunctionName(); for (TypeReference typeReference : new_name.getPrecedence().getTrList()) { tpo.insteadofTraitNameList.add(typeReference.getName()); } useTrait.getPrecedenceMap().put(tpo.traitMethodName, tpo); } } return useTrait; } public static IField[] getTraitFields(IType type, Set<String> nameSet) { UseTrait useTrait = parse(type); List<IField> fieldList = new ArrayList<IField>(); Set<String> traitNameSet = new HashSet<String>(); for (String trait : useTrait.getTraits()) { IType[] traitTypes = PHPModelAccess.getDefault().findTraits(trait, MatchRule.EXACT, 0, 0, createSearchScope(type), null); for (IType traitType : traitTypes) { String traitName = PHPModelUtils.getFullName(traitType); if (!trait.equals(traitName)) { continue; } if (traitNameSet.contains(traitName)) { continue; } else { traitNameSet.add(traitName); } IField[] fields; try { fields = PHPModelUtils.getTypeField(traitType, "", false); //$NON-NLS-1$ // fields = traitType.getFields(); for (IField field : fields) { List<IField> aliasList = getFieldWrapper(useTrait, field, type); for (IField alias : aliasList) { if (alias == null) { continue; } String elementName = alias.getElementName(); if (elementName.startsWith("$")) { //$NON-NLS-1$ elementName = elementName.substring(1); } if (!nameSet.contains(elementName)) { fieldList.add(alias); } } } } catch (ModelException e) { } } } return fieldList.toArray(new IField[fieldList.size()]); } private static List<IField> getFieldWrapper(UseTrait useTrait, IField field, IType type) { List<IField> result = new ArrayList<IField>(); String fieldName = field.getElementName(); if (fieldName.startsWith("$")) { //$NON-NLS-1$ fieldName = fieldName.substring(1); } TraitPrecedenceObject tpo = useTrait.getPrecedenceMap().get(fieldName); boolean shouldAddSelf = true; boolean changeVisibility = false; if (tpo != null) { shouldAddSelf = false; } List<TraitAliasObject> aliasList = useTrait.getAliasMap().get(fieldName); String fullName = PHPModelUtils.getFullName(field.getDeclaringType()); if (aliasList != null) { for (TraitAliasObject tao : aliasList) { if ((tao.traitName == null || fullName.equals(tao.traitName))) { IField alias = new FieldWrapper(field, tao.newMethodVisibility, tao.newMethodName, type); result.add(alias); if (fieldName.equals(tao.newMethodName) || tao.newMethodName == null) { changeVisibility = true; } } if (tpo != null) { if (!fullName.equals(tpo.traitName)) { continue; } else { shouldAddSelf = true; } } } } else { if (tpo != null && fullName.equals(tpo.traitName)) { shouldAddSelf = true; } } if (shouldAddSelf && !changeVisibility) { result.add(field); } // TraitAliasObject tao = useTrait.getAliasMap().get(fieldName); return result; } public static IMethod[] getTraitMethods(IType type, Set<String> nameSet) { UseTrait useTrait = parse(type); List<IMethod> fieldList = new ArrayList<IMethod>(); Set<String> traitNameSet = new HashSet<String>(); for (String trait : useTrait.getTraits()) { IType[] traitTypes = PHPModelAccess.getDefault().findTraits(trait, MatchRule.EXACT, 0, 0, createSearchScope(type), null); for (IType traitType : traitTypes) { String traitName = PHPModelUtils.getFullName(traitType); if (!trait.equals(traitName)) { continue; } if (traitNameSet.contains(traitName)) { continue; } else { traitNameSet.add(traitName); } IMethod[] methods; try { methods = PHPModelUtils.getTypeMethod(traitType, "", false); //$NON-NLS-1$ // methods = traitType.getMethods(); for (IMethod method : methods) { List<IMethod> aliasList = getMethodWrapper(useTrait, method, type); for (IMethod alias : aliasList) { if (alias == null) { continue; } String elementName = alias.getElementName(); if (!nameSet.contains(elementName)) { fieldList.add(alias); } } } } catch (ModelException e) { } } } return fieldList.toArray(new IMethod[fieldList.size()]); } private static List<IMethod> getMethodWrapper(UseTrait useTrait, IMethod method, IType type) { List<IMethod> result = new ArrayList<IMethod>(); String methodName = method.getElementName(); TraitPrecedenceObject tpo = useTrait.getPrecedenceMap().get(methodName); boolean shouldAddSelf = true; boolean changeVisibility = false; if (tpo != null) { shouldAddSelf = false; } List<TraitAliasObject> aliasList = useTrait.getAliasMap().get(methodName); String fullName = PHPModelUtils.getFullName(method.getDeclaringType()); if (aliasList != null) { for (TraitAliasObject tao : aliasList) { if ((tao.traitName == null || fullName.equals(tao.traitName))) { IMethod alias = new MethodWrapper(method, tao.newMethodVisibility, tao.newMethodName, type); result.add(alias); if (methodName.equals(tao.newMethodName) || tao.newMethodName == null) { changeVisibility = true; } } if (tpo != null) { if (!fullName.equals(tpo.traitName)) { continue; } else { shouldAddSelf = true; } } } } else { if (tpo != null) { if (fullName.equals(tpo.traitName)) { shouldAddSelf = true; } } } if (shouldAddSelf && !changeVisibility) { result.add(method); } // TraitAliasObject tao = useTrait.getAliasMap().get(fieldName); return result; } /** * Creates search scope */ public static IDLTKSearchScope createSearchScope(IType type) { ISourceModule sourceModule = type.getSourceModule(); IScriptProject scriptProject = sourceModule.getScriptProject(); if (scriptProject != null) { return SearchEngine.createSearchScope(scriptProject); } IProjectFragment projectFragment = (IProjectFragment) sourceModule.getAncestor(IModelElement.PROJECT_FRAGMENT); if (projectFragment != null) { return SearchEngine.createSearchScope(projectFragment); } return SearchEngine.createSearchScope(sourceModule); } public static interface ITraitMember { public String getRealName(); public IType getHostType(); public boolean useAlias(); } private static class FieldWrapper extends SourceField implements ITraitMember { private int flags = -1; private String name; private IMember member; private IType type; public FieldWrapper(IMember member, int flags, String name, IType type) { super((ModelElement) member.getParent(), member.getElementName()); this.member = member; if (!name.startsWith("$")) { //$NON-NLS-1$ name = "$" + name; //$NON-NLS-1$ } this.name = name; this.type = type; try { this.flags = member.getFlags(); if (flags != -1) { if (PHPFlags.isPrivate(this.flags)) { this.flags = this.flags ^ Modifiers.AccPrivate; } else if (PHPFlags.isProtected(this.flags)) { this.flags = this.flags ^ Modifiers.AccProtected; } else if (PHPFlags.isPublic(this.flags)) { this.flags = this.flags ^ Modifiers.AccPublic; } this.flags = this.flags | flags; } } catch (ModelException e) { } } public int getFlags() throws ModelException { if (flags != -1) { return flags; } return member.getFlags(); } public ISourceRange getNameRange() throws ModelException { return member.getNameRange(); } public ISourceRange getSourceRange() throws ModelException { return member.getSourceRange(); } @Override public String getElementName() { if (name != null) { return name; } return member.getElementName(); } public String getRealName() { return member.getElementName(); } public IType getHostType() { return type; } public boolean useAlias() { return name != null && !name.equals(member.getElementName()); } } private static class MethodWrapper extends SourceMethod implements ITraitMember { private int flags = -1; private String name; private IMethod member; private IType type; public MethodWrapper(IMethod member, int flags, String name, IType type) { super((ModelElement) member.getParent(), member.getElementName()); this.member = member; this.name = name; this.type = type; try { this.flags = member.getFlags(); if (flags != -1) { if (PHPFlags.isPrivate(this.flags)) { this.flags = this.flags ^ Modifiers.AccPrivate; } else if (PHPFlags.isProtected(this.flags)) { this.flags = this.flags ^ Modifiers.AccProtected; } else if (PHPFlags.isPublic(this.flags)) { this.flags = this.flags ^ Modifiers.AccPublic; } this.flags = this.flags | flags; } } catch (ModelException e) { } } public int getFlags() throws ModelException { if (flags != -1) { return flags; } return member.getFlags(); } public ISourceRange getNameRange() throws ModelException { return member.getNameRange(); } public ISourceRange getSourceRange() throws ModelException { return member.getSourceRange(); } @Override public String getElementName() { if (name != null) { return name; } return member.getElementName(); } public String getRealName() { return member.getElementName(); } public IType getHostType() { return type; } public IParameter[] getParameters() throws ModelException { return member.getParameters(); } @Override public String[] getParameterNames() throws ModelException { return member.getParameterNames(); } public boolean isConstructor() throws ModelException { return false; } public boolean useAlias() { return name != null && !name.equals(member.getElementName()); } } }