/**
   * Gets the cookie of the first available inderxed method
   *
   * @param cookieType Class of the Cookie
   * @return Cookie of indexedGetter or indexedSetter MethodElement
   */
  public Node.Cookie getCookie(Class cookieType) {
    if (indexedGetterMethod != null) return indexedGetterMethod.getCookie(cookieType);

    if (indexedSetterMethod != null) return indexedSetterMethod.getCookie(cookieType);

    return super.getCookie(cookieType);
  }
  /**
   * Sets the indexed type of IdxPropertyPattern
   *
   * @param type New indexed type of the indexed property
   * @throws SourceException If the modification of source code is impossible
   */
  public void setIndexedType(Type type) throws SourceException {

    if (this.indexedType.compareTo(type, true)) return;

    // Remember the old type & old indexed type
    Type oldIndexedType = this.indexedType;
    Type oldType = this.type;

    // Change the indexed type
    if (indexedGetterMethod != null) {
      indexedGetterMethod.setReturn(type);
    }
    if (indexedSetterMethod != null) {
      MethodParameter[] params = indexedSetterMethod.getParameters();
      if (params.length > 1) {
        params[1].setType(type);
        indexedSetterMethod.setParameters(params);
      }
    }

    // Test if the old type of getter and seter was an array of indexedType
    // if so change the type of that array.
    if (oldType != null
        && oldType.isArray()
        && oldType.getElementType().compareTo(oldIndexedType, false)) {
      Type newArrayType = Type.createArray(type);
      super.setType(newArrayType);
    }

    indexedType = type;
  }
  /**
   * Generates indexed getter method with body and optionaly with Javadoc comment.
   *
   * @param body Body of the method
   * @param javadoc Generate Javadoc comment?
   * @throws SourceException If modification of source code is impossible.
   */
  void generateIndexedGetterMethod(String body, boolean javadoc) throws SourceException {

    ClassElement declaringClass = getDeclaringClass();
    MethodElement newGetter = new MethodElement();
    MethodParameter[] newParameters = {new MethodParameter("index", Type.INT, false)}; // NOI18N

    newGetter.setName(Identifier.create("get" + capitalizeFirstLetter(getName()))); // NOI18N
    newGetter.setReturn(indexedType);
    newGetter.setModifiers(Modifier.PUBLIC);
    newGetter.setParameters(newParameters);
    if (declaringClass.isInterface()) {
      newGetter.setBody(null);
    } else if (body != null) newGetter.setBody(body);

    if (javadoc) {
      String comment =
          MessageFormat.format(
              bundle.getString("COMMENT_IdxPropertyGetter"), new Object[] {getName()});
      newGetter.getJavaDoc().setRawText(comment);
    }

    // System.out.println ("Generating getter" ); // NOI18N

    if (declaringClass == null) throw new SourceException();
    else {
      // System.out.println ( "Adding getter method" ); // NOI18N
      declaringClass.addMethod(newGetter);
      indexedGetterMethod =
          declaringClass.getMethod(newGetter.getName(), getParameterTypes(newGetter));
    }
  }
  /**
   * Based on names of indexedGetter and indexedSetter resolves the name of the indexed property.
   *
   * @return Name of the indexed property
   */
  String findIndexedPropertyName() {

    String superName = findPropertyName();

    if (superName == null) {
      String methodName = null;

      if (indexedGetterMethod != null) methodName = indexedGetterMethod.getName().getName();
      else if (indexedSetterMethod != null) methodName = indexedSetterMethod.getName().getName();
      else throw new InternalError("Indexed property with all methods == null"); // NOI18N

      return methodName.startsWith("is")
          ? // NOI18N
          Introspector.decapitalize(methodName.substring(2))
          : Introspector.decapitalize(methodName.substring(3));
    } else return superName;
  }
  /**
   * Resolves the indexed type of the property from type of getter and setter. Chcecks for
   * conformance to Beans design patterns.
   *
   * @throws IntrospectionException if the property doesnt folow the design patterns
   */
  private void findIndexedPropertyType() throws IntrospectionException {

    indexedType = null;

    if (indexedGetterMethod != null) {
      MethodParameter[] params = indexedGetterMethod.getParameters();
      if (params.length != 1) {
        throw new IntrospectionException("bad indexed read method arg count"); // NOI18N
      }
      if (!params[0].getType().compareTo(Type.INT, false)) {
        throw new IntrospectionException("not int index to indexed read method"); // NOI18N
      }
      indexedType = indexedGetterMethod.getReturn();
      if (indexedType.compareTo(Type.VOID, false)) {
        throw new IntrospectionException("indexed read method return void"); // NOI18N
      }
    }

    if (indexedSetterMethod != null) {
      MethodParameter params[] = indexedSetterMethod.getParameters();
      if (params.length != 2) {
        throw new IntrospectionException("bad indexed write method arg count"); // NOI18N
      }
      if (!params[0].getType().compareTo(Type.INT, false)) {
        throw new IntrospectionException("non int index to indexed write method"); // NOI18N
      }
      if (indexedType != null && !indexedType.compareTo(params[1].getType(), false)) {
        throw new IntrospectionException(
            "type mismatch between indexed read and write methods"); // NOI18N
      }
      indexedType = params[1].getType();
    }

    // type = indexedType;

    Type propType = getType();
    if (propType != null
        && (!propType.isArray() || !propType.getElementType().compareTo(indexedType, false))) {
      throw new IntrospectionException(
          "type mismatch between property type and indexed type"); // NOI18N
    }
  }
  /**
   * Sets the name of IdxPropertyPattern
   *
   * @param name New name of the property.
   * @throws SourceException If the modification of source code is impossible.
   */
  public void setName(String name) throws SourceException {
    super.setName(name);

    name = capitalizeFirstLetter(name);

    if (indexedGetterMethod != null) {
      Identifier idxGetterMethodID =
          Identifier.create(
              (indexedGetterMethod.getName().getName().startsWith("get")
                      ? // NOI18N
                      "get"
                      : "is")
                  + name); // NOI18N
      indexedGetterMethod.setName(idxGetterMethodID);
    }
    if (indexedSetterMethod != null) {
      Identifier idxSetterMethodID = Identifier.create("set" + name); // NOI18N
      indexedSetterMethod.setName(idxSetterMethodID);
    }
  }
  /**
   * Sets the non-indexed type of IdxPropertyPattern
   *
   * @param type New non-indexed type of the indexed property
   * @throws SourceException If the modification of source code is impossible
   */
  public void setType(Type type) throws SourceException {

    if (this.type != null && this.type.compareTo(type, true)) return;

    // Remember the old type & old indexed type
    Type oldIndexedType = this.indexedType;
    Type oldType = this.type;

    if (oldType == null) {
      this.type = type;
      oldType = type;
      int mode = getMode();
      if (mode == READ_WRITE || mode == READ_ONLY) generateGetterMethod();
      if (mode == READ_WRITE || mode == WRITE_ONLY) generateSetterMethod();
    } else
      // Change the type
      super.setType(type);

    // Test if the idexedType is the type of array and change it if so
    if (type.isArray()
        && oldType.isArray()
        && oldType.getElementType().compareTo(oldIndexedType, false)) {
      Type newType = type.getElementType();

      if (indexedGetterMethod != null) {
        indexedGetterMethod.setReturn(newType);
      }
      if (indexedSetterMethod != null) {
        MethodParameter[] params = indexedSetterMethod.getParameters();
        if (params.length > 1) {
          params[1].setType(newType);
          indexedSetterMethod.setParameters(params);
        }
      }

      // Set the type  to new type
      setIndexedType(newType);
    }
  }
  /**
   * Generates indexed setter method with body and optionaly with Javadoc comment.
   *
   * @param body Body of the method
   * @param javadoc Generate Javadoc comment?
   * @param constrained Is the property constrained?
   * @throws SourceException If modification of source code is impossible.
   */
  void generateIndexedSetterMethod(String body, boolean constrained, boolean javadoc)
      throws SourceException {

    ClassElement declaringClass = getDeclaringClass();
    MethodElement newSetter = new MethodElement();
    MethodParameter[] newParameters = {
      new MethodParameter("index", Type.INT, false), // NOI18N
      new MethodParameter(name, indexedType, false)
    };

    newSetter.setName(Identifier.create("set" + capitalizeFirstLetter(getName()))); // NOI18N
    newSetter.setReturn(Type.VOID);
    newSetter.setModifiers(Modifier.PUBLIC);
    newSetter.setParameters(newParameters);
    if (constrained)
      newSetter.setExceptions(
          (new Identifier[] {Identifier.create("java.beans.PropertyVetoException")})); // NOI18N
    if (declaringClass.isInterface()) {
      newSetter.setBody(null);
    } else if (body != null) {
      newSetter.setBody(body);
    }

    if (javadoc) {
      String comment =
          MessageFormat.format(
              bundle.getString("COMMENT_IdxPropertySetter"), new Object[] {getName(), name});
      if (constrained) comment = comment + bundle.getString("COMMENT_Tag_ThrowsPropertyVeto");
      newSetter.getJavaDoc().setRawText(comment);
    }

    if (declaringClass == null) throw new SourceException();
    else {
      declaringClass.addMethod(newSetter);
      indexedSetterMethod =
          declaringClass.getMethod(newSetter.getName(), getParameterTypes(newSetter));
    }
  }