All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gw.lang.reflect.FeatureManager Maven / Gradle / Ivy

There is a newer version: 1.18.2
Show newest version
/*
 * Copyright 2014 Guidewire Software, Inc.
 */

package gw.lang.reflect;

import gw.config.CommonServices;
import gw.lang.parser.CICS;
import gw.lang.reflect.gs.IGenericTypeVariable;
import gw.lang.reflect.gs.IGosuClass;
import gw.lang.reflect.gs.IGosuEnhancement;
import gw.lang.reflect.java.IJavaType;
import gw.lang.reflect.java.JavaTypes;
import gw.lang.reflect.module.IModule;
import gw.util.DynamicArray;
import gw.util.GosuExceptionUtil;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@SuppressWarnings({"unchecked"})
public class FeatureManager {

  private final boolean _caseSensitive;
  private final boolean _addObjectMethods;
  private IRelativeTypeInfo _typeInfo;
  private volatile Map _methodsInitialized = new HashMap();
  private volatile Map _propertiesInitialized = new HashMap();
  private volatile InitState _ctorsInitialized = InitState.NotInitialized;
  private Map[]> _properties = new HashMap[]>();
  private Map _methods = new HashMap();
  private List[] _constructors = new List[IRelativeTypeInfo.Accessibility_Size];
  private String _superPropertyPrefix;
  private IType _supertypeToCopyPropertiesFrom;
  public FeatureManager(IRelativeTypeInfo typeInfo, boolean caseSensitive) {
    this(typeInfo, caseSensitive, false);
  }

  public FeatureManager(IRelativeTypeInfo typeInfo, boolean caseSensitive, boolean addObjectMethods) {
    _typeInfo = typeInfo;
    _caseSensitive = caseSensitive;// && !ILanguageLevel.Util.STANDARD_GOSU();
    _addObjectMethods = addObjectMethods;
  }

  public static IRelativeTypeInfo.Accessibility getAccessibilityForClass( IType ownersClass, IType whosAskin )
  {
    if( TypeSystem.isIncludeAll() )
    {
      return IRelativeTypeInfo.Accessibility.PRIVATE;
    }

    if( ownersClass == null || whosAskin == null )
    {
      return IRelativeTypeInfo.Accessibility.PUBLIC;
    }

    if( getTopLevelTypeName( whosAskin ).equals( getTopLevelTypeName( ownersClass ) ) )
    {
      // Implies private members, which means everything.
      return IRelativeTypeInfo.Accessibility.PRIVATE;
    }
    else if( Modifier.isPrivate( ownersClass.getModifiers() ) )
    {
      return IRelativeTypeInfo.Accessibility.NONE;
    }
    else if( isInSameNamespace( ownersClass, whosAskin ) )
    {
      return IRelativeTypeInfo.Accessibility.INTERNAL;
    }
    else if( Modifier.isInternal( ownersClass.getModifiers() ) )
    {
      return IRelativeTypeInfo.Accessibility.NONE;
    }
    else if( isInEnclosingClassHierarchy( ownersClass, whosAskin ) )
    {
      return IRelativeTypeInfo.Accessibility.PROTECTED;
    }

    return IRelativeTypeInfo.Accessibility.PUBLIC;
  }

  public static boolean isInSameNamespace(IType ownersClass, IType whosAskin) {
    ownersClass = IGosuClass.ProxyUtil.getProxiedType( ownersClass );
    whosAskin = IGosuClass.ProxyUtil.getProxiedType( whosAskin );
    String whosAskinNamespace = getTopLevelEnclosingClassNamespace(whosAskin);
    return whosAskinNamespace != null &&
           whosAskinNamespace.equals( getTopLevelEnclosingClassNamespace( ownersClass ) );
  }

  private static String getTopLevelEnclosingClassNamespace(IType type) {
    IType topLevelClass = type;
    while (topLevelClass.getEnclosingType() != null) {
      topLevelClass = topLevelClass.getEnclosingType();
    }
    return topLevelClass.getNamespace();
  }

  public static boolean isInEnclosingClassHierarchy(IType ownersClass, IType whosAskin) {
    return whosAskin != null && ownersClass != null &&
           (isInHierarchy(ownersClass, whosAskin) ||
            isInEnhancedTypesHierarchy(ownersClass, whosAskin) ||
            isInEnclosingClassHierarchy(ownersClass, whosAskin.getEnclosingType()));
  }

  protected static boolean isInEnhancedTypesHierarchy(IType ownersClass, IType whosAskin) {
    return (whosAskin instanceof IGosuEnhancement &&
           ((IGosuEnhancement)whosAskin).getEnhancedType() != null &&
           ownersClass.isAssignableFrom(((IGosuEnhancement) whosAskin).getEnhancedType()));
  }

  protected static boolean isInHierarchy(IType ownersClass, IType whosAskin) {
    return ownersClass.isAssignableFrom(whosAskin) ||
           ownersClass.getEnclosingType() != null && isInHierarchy( ownersClass.getEnclosingType(), whosAskin ) ||
           (ownersClass instanceof IGosuClass && ((IGosuClass) ownersClass).isSubClass( whosAskin ));
  }

  private static String getTopLevelTypeName( IType type )
  {
    while( type.getEnclosingType() != null )
    {
      type = TypeSystem.getPureGenericType( type.getEnclosingType() );
    }
    return TypeSystem.getPureGenericType( type ).getName();
  }

  public static boolean isFeatureAccessible(IAttributedFeatureInfo property, IRelativeTypeInfo.Accessibility accessibility) {
    boolean isAccessible = false;
    switch (accessibility) {
      case NONE:
        break;
      case PUBLIC:
        if (property.isPublic()) {
          isAccessible = true;
        }
        break;
      case PROTECTED:
        if (property.isPublic() || property.isProtected()) {
          isAccessible = true;
        }
        break;
      case INTERNAL:
        if (property.isPublic() || property.isInternal() || property.isProtected()) {
          isAccessible = true;
        }
        break;
      case PRIVATE:
        if (property.isPublic() || property.isInternal() || property.isProtected() || property.isPrivate()) {
          isAccessible = true;
        }
        break;
    }
    return isAccessible;
  }

  public void clear() {
    _methodsInitialized = new HashMap();
    _propertiesInitialized = new HashMap();
    _ctorsInitialized = InitState.NotInitialized;
    clearMaps();
  }

  private void clearMaps() {
    for(PropertyNameMap[] properties : _properties.values()) {
      for (int i = 0; i < properties.length; i++) {
        properties[i] = null;
      }
    }
    for (int i = 0; i < _constructors.length; i++) {
      _constructors[i] = null;
    }
    for(MethodList[] methods : _methods.values()) {
      for (int i = 0; i < methods.length; i++) {
        methods[i] = null;
      }
    }
  }

  private void clearProperties(IModule module) {
    PropertyNameMap[] properties = _properties.get(module);
    if(properties != null) {
      for (int i = 0; i < properties.length; i++) {
        properties[i] = null;
      }
    }
  }

  private void clearMethods(IModule module) {
    MethodList[] methods = _methods.get(module);
    if(methods != null) {
      for (int i = 0; i < methods.length; i++) {
        methods[i] = null;
      }
    }
  }

  private void clearCtors() {
    for (int i = 0; i < _constructors.length; i++) {
      _constructors[i] = null;
    }
  }

  public List getProperties( IRelativeTypeInfo.Accessibility accessibility ) {
    maybeInitProperties();
    PropertyNameMap[] arr = _properties.get( TypeSystem.getCurrentModule() );
    if( arr == null )
    {
      return Collections.emptyList();
    }
    PropertyNameMap props = arr[accessibility.ordinal()];
    return (List) (props == null ? Collections.emptyList() : props.values());
  }

  public IPropertyInfo getProperty( IRelativeTypeInfo.Accessibility accessibility, CharSequence propName ) {
    maybeInitProperties();
    PropertyNameMap[] arr = _properties.get( TypeSystem.getCurrentModule() );
    if( arr == null )
    {
      return null;
    }
    PropertyNameMap accessMap = arr[accessibility.ordinal()];
    return accessMap == null ? null : accessMap.get(convertCharSequenceToCorrectSensitivity(propName));
  }

  private T convertCharSequenceToCorrectSensitivity(CharSequence propName) {
    return (T) (_caseSensitive ? propName == null ?  "": propName.toString() : CICS.get( propName ));
  }

  @SuppressWarnings({"unchecked"})
  public MethodList getMethods( IRelativeTypeInfo.Accessibility accessibility) {
    maybeInitMethods();
    MethodList[] arr = _methods.get( TypeSystem.getCurrentModule() );
    if( arr == null )
    {
      return MethodList.EMPTY;
    }
    MethodList iMethodInfos = arr[accessibility.ordinal()];
    return iMethodInfos == null ? MethodList.EMPTY : iMethodInfos;
  }

  public IMethodInfo getMethod( IRelativeTypeInfo.Accessibility accessibility, CharSequence methodName, IType... params ) {
    maybeInitMethods();
    return ITypeInfo.FIND.method( getMethods( accessibility ), methodName, params );
  }

  @SuppressWarnings({"unchecked"})
  public List getConstructors( IRelativeTypeInfo.Accessibility accessibility ) {
    maybeInitConstructors();
    List list = _constructors[accessibility.ordinal()];
    return (List) (list == null ? Collections.emptyList() : list);
  }

  public IConstructorInfo getConstructor( IRelativeTypeInfo.Accessibility accessibility, IType[] params ) {
    maybeInitConstructors();
    return ITypeInfo.FIND.constructor( getConstructors(accessibility), params );
  }

  @SuppressWarnings({"ConstantConditions"})
  protected void maybeInitMethods() {
    IModule module = TypeSystem.getCurrentModule();
    if (module == null) {
      throw new NullPointerException("Cannot init the FeatureManager with no current module.");
    }
    if (_methodsInitialized.get(module) != InitState.Initialized && _methodsInitialized.get(module) != InitState.ERROR) {
      TypeSystem.lock();
      try {
        if (_methodsInitialized.get(module) != InitState.Initialized) {
          if (_methodsInitialized.get(module) == InitState.Initializing) {
            throw new IllegalStateException("Methods for " + _typeInfo.getOwnersType() + " are cyclic.");
          }
          _methodsInitialized.put(module, InitState.Initializing);
          clearMethods( module );
          try {
            MethodList[] methods = new MethodList[IRelativeTypeInfo.Accessibility_Size];
            {
              MethodList privateMethods = new MethodList();
              if( _addObjectMethods ) {
                mergeMethods( privateMethods, convertType( JavaTypes.OBJECT() ), false );
              }
              if( _typeInfo == null ) {
                throw new IllegalStateException( "Null TypeInfo" );
              }
              if( _typeInfo.getOwnersType() == null ) {
                throw new IllegalStateException( "null owner" );
              }
              if( _typeInfo.getOwnersType().getInterfaces() == null ) {
                throw new IllegalStateException( "null interfaces for " + _typeInfo.getOwnersType().getName() );
              }
              for (IType type : _typeInfo.getOwnersType().getInterfaces()) {
                mergeMethods( privateMethods, convertType( type ), false );
              }
              if ( getSuperType() != null) {
                mergeMethods(privateMethods, convertType( getSuperType() ), true);
              }
              List declaredMethods = _typeInfo.getDeclaredMethods();
              for (IMethodInfo methodInfo : declaredMethods) {
                mergeMethod(privateMethods, methodInfo, true);
              }
              addEnhancementMethods(privateMethods);
              privateMethods.trimToSize();
//              privateMethods = Collections.unmodifiableList(privateMethods);
              // The size checking madness is to save memory.  If the lists/maps are the same then reuse.
              methods[IRelativeTypeInfo.Accessibility.PRIVATE.ordinal()] = privateMethods;
              methods[IRelativeTypeInfo.Accessibility.PROTECTED.ordinal()] = privateMethods.filterMethods( IRelativeTypeInfo.Accessibility.PROTECTED );
              if (methods[IRelativeTypeInfo.Accessibility.PROTECTED.ordinal()].size() == privateMethods.size()) {
                methods[IRelativeTypeInfo.Accessibility.PROTECTED.ordinal()] = privateMethods;
              }
              methods[IRelativeTypeInfo.Accessibility.INTERNAL.ordinal()] = privateMethods.filterMethods( IRelativeTypeInfo.Accessibility.INTERNAL );
              if (methods[IRelativeTypeInfo.Accessibility.INTERNAL.ordinal()].size() == methods[IRelativeTypeInfo.Accessibility.PROTECTED.ordinal()].size()) {
                methods[IRelativeTypeInfo.Accessibility.INTERNAL.ordinal()] = methods[IRelativeTypeInfo.Accessibility.PROTECTED.ordinal()];
              }
              methods[IRelativeTypeInfo.Accessibility.PUBLIC.ordinal()] = privateMethods.filterMethods( IRelativeTypeInfo.Accessibility.PUBLIC );
              if (methods[IRelativeTypeInfo.Accessibility.PUBLIC.ordinal()].size() == methods[IRelativeTypeInfo.Accessibility.INTERNAL.ordinal()].size()) {
                methods[IRelativeTypeInfo.Accessibility.PUBLIC.ordinal()] = methods[IRelativeTypeInfo.Accessibility.INTERNAL.ordinal()];
              }
              methods[IRelativeTypeInfo.Accessibility.NONE.ordinal()] = MethodList.EMPTY;
            }
            _methods.put(module, methods);

            _methodsInitialized.put(module, InitState.Initialized);
          } finally {
            if (_methodsInitialized.get(module) != InitState.Initialized) {
              _methodsInitialized.put(module, InitState.ERROR);
            }
          }
        }
      } catch ( Exception ex ) {
        ex.printStackTrace(); // exception is swallowed by source diff handler? print it again here
        throw GosuExceptionUtil.forceThrow( ex, _typeInfo.getOwnersType().getName() );
      } finally {
        TypeSystem.unlock();
      }
    }
  }

  @SuppressWarnings({"ConstantConditions"})
  protected void maybeInitProperties() {
    maybeInitMethods(); // because properties depend on methods for their getter or setter method !

    IModule module = TypeSystem.getCurrentModule();
    if (module == null) {
      throw new NullPointerException("Cannot init the FeatureManager with no current module.");
    }
    if (_propertiesInitialized.get(module) != InitState.Initialized && _propertiesInitialized.get(module) != InitState.ERROR) {
      TypeSystem.lock();
      try {
        if (_propertiesInitialized.get(module) != InitState.Initialized) {
          if (_propertiesInitialized.get(module) == InitState.Initializing) {
            throw new IllegalStateException("Properties for " + _typeInfo.getOwnersType() + " are cyclic.");
          }
          _propertiesInitialized.put(module, InitState.Initializing);
          clearProperties(module);
          try {
            PropertyNameMap[] properties = new PropertyNameMap[IRelativeTypeInfo.Accessibility_Size];
            {
              PropertyNameMap privateProps = new PropertyNameMap();
              for (IType type : _typeInfo.getOwnersType().getInterfaces()) {
                mergeProperties(privateProps, convertType(type), false);
              }
              IType supertype = getSuperType();
              if ( supertype != null ) {
                mergeProperties( privateProps, convertType( supertype ), true );
              }

              List declaredProperties = (List) _typeInfo.getDeclaredProperties();
              for (IPropertyInfo property : declaredProperties) {
                mergeProperty(privateProps, property, true);
              }
              addEnhancementProperties(privateProps, _caseSensitive);
              privateProps.freeze();
              // The size checking madness is to save memory.  If the lists/maps are the same then reuse.
              properties[IRelativeTypeInfo.Accessibility.PRIVATE.ordinal()] = privateProps;
              properties[IRelativeTypeInfo.Accessibility.PROTECTED.ordinal()] = convertToMap(filterFeatures(privateProps.values(), IRelativeTypeInfo.Accessibility.PROTECTED));
              if (properties[IRelativeTypeInfo.Accessibility.PROTECTED.ordinal()].size() == privateProps.size()) {
                properties[IRelativeTypeInfo.Accessibility.PROTECTED.ordinal()] = privateProps;
              }
              properties[IRelativeTypeInfo.Accessibility.INTERNAL.ordinal()] = convertToMap(filterFeatures(privateProps.values(), IRelativeTypeInfo.Accessibility.INTERNAL));
              if (properties[IRelativeTypeInfo.Accessibility.INTERNAL.ordinal()].size() == properties[IRelativeTypeInfo.Accessibility.PROTECTED.ordinal()].size()) {
                properties[IRelativeTypeInfo.Accessibility.INTERNAL.ordinal()] = properties[IRelativeTypeInfo.Accessibility.PROTECTED.ordinal()];
              }
              properties[IRelativeTypeInfo.Accessibility.PUBLIC.ordinal()] = convertToMap(filterFeatures(privateProps.values(), IRelativeTypeInfo.Accessibility.PUBLIC));
              if (properties[IRelativeTypeInfo.Accessibility.PUBLIC.ordinal()].size() == properties[IRelativeTypeInfo.Accessibility.INTERNAL.ordinal()].size()) {
                properties[IRelativeTypeInfo.Accessibility.PUBLIC.ordinal()] = properties[IRelativeTypeInfo.Accessibility.INTERNAL.ordinal()];
              }
              properties[IRelativeTypeInfo.Accessibility.NONE.ordinal()] = new PropertyNameMap();
            }
            _properties.put(module, properties);

            _propertiesInitialized.put(module, InitState.Initialized);
          } finally {
            if (_propertiesInitialized.get(module) != InitState.Initialized) {
              _propertiesInitialized.put(module, InitState.ERROR);
            }
          }
        }
      } catch ( Exception ex ) {
        ex.printStackTrace(); // exception is swallowed by source diff handler? print it again here
        throw GosuExceptionUtil.forceThrow( ex, _typeInfo.getOwnersType().getName() );
      } finally {
        TypeSystem.unlock();
      }
    }
  }

  private IType getSuperType() {
    IType ownersType = _typeInfo.getOwnersType();
    IType supertype = ownersType.getSupertype();
    if( supertype == null && ownersType instanceof IJavaType && !_typeInfo.getOwnersType().equals( JavaTypes.OBJECT() ) ) {
      supertype = JavaTypes.OBJECT();
    }
    return supertype;
  }

  @SuppressWarnings({"ConstantConditions"})
  protected void maybeInitConstructors() {
    if (_ctorsInitialized != InitState.Initialized && _ctorsInitialized != InitState.ERROR) {
      TypeSystem.lock();
      try {
        if (_ctorsInitialized != InitState.Initialized) {
          if (_ctorsInitialized == InitState.Initializing) {
            throw new IllegalStateException("Constructors for " + _typeInfo.getOwnersType() + " are cyclic.");
          }
          _ctorsInitialized = InitState.Initializing;
          clearCtors();
          try {
            if(_ctorsInitialized != InitState.Initialized && _ctorsInitialized != InitState.ERROR) {
              clearCtors();
              try {

                List[] constructors = new List[IRelativeTypeInfo.Accessibility_Size];
                {
                  List privateConstructors = new ArrayList( _typeInfo.getDeclaredConstructors());
                  ((ArrayList) privateConstructors).trimToSize();
                  privateConstructors = Collections.unmodifiableList(privateConstructors);
                  constructors[IRelativeTypeInfo.Accessibility.PRIVATE.ordinal()] = Collections.unmodifiableList(privateConstructors);
                  constructors[IRelativeTypeInfo.Accessibility.PROTECTED.ordinal()] = Collections.unmodifiableList((List) filterFeatures(privateConstructors, IRelativeTypeInfo.Accessibility.PROTECTED));
                  if (constructors[IRelativeTypeInfo.Accessibility.PROTECTED.ordinal()].size() == privateConstructors.size()) {
                    constructors[IRelativeTypeInfo.Accessibility.PROTECTED.ordinal()] = privateConstructors;
                  }
                  constructors[IRelativeTypeInfo.Accessibility.INTERNAL.ordinal()] = Collections.unmodifiableList((List) filterFeatures(privateConstructors, IRelativeTypeInfo.Accessibility.INTERNAL));
                  if (constructors[IRelativeTypeInfo.Accessibility.INTERNAL.ordinal()].size() == constructors[IRelativeTypeInfo.Accessibility.PROTECTED.ordinal()].size()) {
                    constructors[IRelativeTypeInfo.Accessibility.INTERNAL.ordinal()] = constructors[IRelativeTypeInfo.Accessibility.PROTECTED.ordinal()];
                  }
                  constructors[IRelativeTypeInfo.Accessibility.PUBLIC.ordinal()] = Collections.unmodifiableList((List) filterFeatures(privateConstructors, IRelativeTypeInfo.Accessibility.PUBLIC));
                  if (constructors[IRelativeTypeInfo.Accessibility.PUBLIC.ordinal()].size() == constructors[IRelativeTypeInfo.Accessibility.INTERNAL.ordinal()].size()) {
                    constructors[IRelativeTypeInfo.Accessibility.PUBLIC.ordinal()] = constructors[IRelativeTypeInfo.Accessibility.INTERNAL.ordinal()];
                  }
                  constructors[IRelativeTypeInfo.Accessibility.NONE.ordinal()] = Collections.emptyList();
                }
                _constructors = constructors;
                _ctorsInitialized = InitState.Initialized;
              } finally {
                if(_ctorsInitialized != InitState.Initialized) {
                  _ctorsInitialized = InitState.ERROR;
                }
              }
            }

            _ctorsInitialized = InitState.Initialized;
          } finally {
            if (_ctorsInitialized != InitState.Initialized) {
              _ctorsInitialized = InitState.ERROR;
            }
          }
        }
      } catch ( Exception ex ) {
//        ex.printStackTrace(); // exception is swallowed by source diff handler? print it again here
        throw GosuExceptionUtil.forceThrow( ex, _typeInfo.getOwnersType().getName() );
      } finally {
        TypeSystem.unlock();
      }
    }
  }

  protected IType convertType(IType type) {
    return type;
  }

  protected void addEnhancementMethods(List privateMethods) {
    CommonServices.getEntityAccess().addEnhancementMethods( _typeInfo.getOwnersType(), privateMethods );
  }

  protected void addEnhancementProperties(PropertyNameMap privateProps, boolean caseSensitive) {
    CommonServices.getEntityAccess().addEnhancementProperties(_typeInfo.getOwnersType(), privateProps, caseSensitive );
  }

  public void setSuperPropertyPrefix( String superPropertyPrefix ) {
    _superPropertyPrefix = superPropertyPrefix;
  }

  public void setSupertypeToCopyPropertiesFrom( IType supertypeToCopyPropertiesFrom ) {
    _supertypeToCopyPropertiesFrom = supertypeToCopyPropertiesFrom;
  }

  private PropertyNameMap convertToMap(List features) {
    PropertyNameMap ret = new PropertyNameMap();
    for (IPropertyInfo feature : features) {
      ret.put(convertCharSequenceToCorrectSensitivity(feature.getName()), feature);
    }
    ret.freeze();
    return ret;
  }

  private List filterFeatures(List props, IRelativeTypeInfo.Accessibility accessibility) {
    ArrayList ret = new ArrayList();
    for (Object o : props) {
      IAttributedFeatureInfo property = (IAttributedFeatureInfo) o;
      if (isFeatureAccessible(property, accessibility)) {
        ret.add(property);
      }
    }
    ret.trimToSize();
    return ret;
  }

  protected void mergeProperties(PropertyNameMap props, IType type, boolean replace) {
    if( type != null )
    {
      List propertyInfos;
      if (type.getTypeInfo() instanceof IRelativeTypeInfo) {
        propertyInfos = ((IRelativeTypeInfo) type.getTypeInfo()).getProperties( _typeInfo.getOwnersType());
      } else {
        propertyInfos = type.getTypeInfo().getProperties();
      }
      for (IPropertyInfo propertyInfo : propertyInfos) {
        IType ownersType = propertyInfo.getOwnersType();
        if ( _supertypeToCopyPropertiesFrom == null || ownersType.isAssignableFrom( _supertypeToCopyPropertiesFrom ) || ownersType instanceof IGosuEnhancement ) {
          mergeProperty( props, propertyInfo, replace );
        }
      }
    }
  }

  protected void mergeProperty(PropertyNameMap props, IPropertyInfo propertyInfo, boolean replace) {
    boolean prependPrefix = _superPropertyPrefix != null && ! propertyInfo.getOwnersType().equals( _typeInfo.getOwnersType() );
    T cs = convertCharSequenceToCorrectSensitivity( prependPrefix ? ( _superPropertyPrefix + propertyInfo.getName() ) : propertyInfo.getName() );
    if ( replace || shouldReplace( props, cs, propertyInfo ) ) {
      if ( prependPrefix ) {
        props.put( cs, new PropertyInfoDelegate( propertyInfo.getContainer(), propertyInfo, cs.toString() ) );
      }
      else {
        props.put(cs, propertyInfo);
      }
    }
  }

  private boolean shouldReplace( PropertyNameMap props, T cs, IPropertyInfo propertyInfo )
  {
    IPropertyInfo pi = props.get( cs );
    if( pi == null )
    {
      return true;
    }

    if( propertyInfo.isReadable() && propertyInfo.isWritable( _typeInfo.getOwnersType() ) )
    {
      return !pi.isReadable() || !pi.isWritable();
    }

    return false;
  }

  protected void mergeMethods(MethodList methods, IType type, boolean replace) {
    List methodInfos;
    if (type != null && !TypeSystem.isDeleted(type)) {
      if (type.getTypeInfo() instanceof IRelativeTypeInfo) {
        methodInfos = ((IRelativeTypeInfo) type.getTypeInfo()).getMethods( _typeInfo.getOwnersType());
      } else {
        methodInfos = type.getTypeInfo().getMethods();
      }

      for (IMethodInfo methodInfo : methodInfos) {
        if( !type.isInterface() || !methodInfo.isStatic() ) { // static interface methods are not inherited
          mergeMethod(methods, methodInfo, replace);
        }
      }
    }
  }

  protected void mergeMethod(MethodList methods, IMethodInfo thisMethodInfo, boolean replace) {
    IType[] paramTypes = null;
    DynamicArray matches = methods.getMethods( thisMethodInfo.getDisplayName() );
    for( int i = 0; i < matches.size(); i++ )
    {
      IMethodInfo superMethodInfo = matches.get( i );
      if( superMethodInfo.getParameters().length == thisMethodInfo.getParameters().length )
      {
        paramTypes = paramTypes == null ? removeGenericMethodParameters( thisMethodInfo ) : paramTypes;
        IType[] superParamTypes = removeGenericMethodParameters( superMethodInfo );
        if( argsEqual( superParamTypes, paramTypes ) )
        {
          if( replace )
          {
            methods.set( methods.indexOf( superMethodInfo ), thisMethodInfo );
          }
          return;
        }
      }
    }
    methods.add(thisMethodInfo);
  }

  private IType[] removeGenericMethodParameters(IMethodInfo thisMethodInfo) {
    IParameterInfo[] parameters = thisMethodInfo.getParameters();
    IType[] paramTypes = new IType[parameters.length];
    List methodTypeVars = null;
    if (thisMethodInfo instanceof IGenericMethodInfo) {
      IGenericTypeVariable[] typeVariables = ((IGenericMethodInfo) thisMethodInfo).getTypeVariables();
      if (typeVariables != null && typeVariables.length > 0) {
        methodTypeVars = new ArrayList();
        for (IGenericTypeVariable typeVariable : typeVariables) {
          ITypeVariableType typeVarType = typeVariable.getTypeVariableDefinition().getType();
          methodTypeVars.add(typeVarType);
        }
      }
    }

    for (int i = 0; i < parameters.length; i++) {
      IParameterInfo parameter = parameters[i];
      IType featureType = parameter.getFeatureType();
      if (methodTypeVars != null) {
        featureType = TypeSystem.boundTypes(featureType, methodTypeVars);
      }
      paramTypes[i] = featureType;
    }

    return paramTypes;
  }

  protected boolean areMethodParamsEqual(IType thisMethodParamType, IType superMethodParamType) {
    return thisMethodParamType.equals(superMethodParamType);
  }

  private boolean argsEqual(IType[] parameters, IType[] parameters1) {
    if (parameters.length == parameters1.length) {
      for (int i = 0; i < parameters.length; i++) {
        IType parameter = parameters[i];
        if ( !areMethodParamsEqual(parameter, parameters1[i]) ) {
          return false;
        }
      }
      return true;
    }
    return false;
  }

  public String toString() {
    return _typeInfo.getOwnersType().getName();
  }

  private enum InitState {
    NotInitialized,
    Initializing,
    ERROR, Initialized
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy