Package org.eclipse.jst.jsp.ui.internal.contentassist

Source Code of org.eclipse.jst.jsp.ui.internal.contentassist.BeanInfoProvider

/*******************************************************************************
* Copyright (c) 2004, 2006 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
*******************************************************************************/
package org.eclipse.jst.jsp.ui.internal.contentassist;

import java.beans.Introspector;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import com.ibm.icu.util.StringTokenizer;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jst.jsp.ui.internal.Logger;

/**
* Navigates the IJavaProject classpath (incl. source) on a given resource and infers bean properties
* given a fully qualified beanname. Bean properties can be retrieved using:
* <code>getRuntimeProperties(IResource baseResource, String typeName)</code>
*
* @plannedfor 1.0
*/
public class BeanInfoProvider implements IBeanInfoProvider {

  public class JavaPropertyDescriptor implements IJavaPropertyDescriptor {
    String fType = null;
    String fName = null;
    boolean fReadable = true;
    boolean fWritable = true;

    public JavaPropertyDescriptor(String name, String type, boolean readable, boolean writable) {
      fName = name;
      fType = type;
      fReadable = readable;
      fWritable = writable;
    }

    public String getDeclaredType() {
      return fType;
    }

    public String getDisplayName() {
      return fName;
    }

    public String getName() {
      return fName;
    }

    public boolean getReadable() {
      return fReadable;
    }

    public boolean getWriteable() {
      return fWritable;
    }
  }

  // looks up encoded type (see Class.getName), and gives you a displayable string
  private HashMap fEncodedTypeMap = null;
  // to avoid repeat properties from showing up
  private HashSet fRepeatMethods = null;

  public BeanInfoProvider() {
    fRepeatMethods = new HashSet();
  }

  /**
   * Returns the inferred properties of a bean based on the project from the baseResource,
   * and the fully qualified name of the bean.
   *
   * @param baseResource the base resource where the bean is being used
   * @param typeName the <i>fully qualified</i> type name (eg. javax.swing.JButton) of the bean
   */
  public IJavaPropertyDescriptor[] getRuntimeProperties(IResource baseResource, String typeName) {
    IJavaProject javaProject = JavaCore.create(baseResource.getProject());
    QualifiedName typeQualifiedName = getTypeQualifiedName(typeName);
    List getMethodResults = new ArrayList();
    List isMethodResults = new ArrayList();
    List setMethodResults = new ArrayList();
    List descriptorResults = new ArrayList();
    try {
      IType type = javaProject.findType(typeQualifiedName.getQualifier() + "." + typeQualifiedName.getLocalName()); //$NON-NLS-1$
      // type must exist
      if(type != null) {
        ITypeHierarchy hierarchy = type.newTypeHierarchy(null);
        IType[] supers = hierarchy.getAllSuperclasses(type);
 
        IMethod[] methods = type.getMethods();
        // iterate the bean's methods
        for (int i = 0; i < methods.length; i++)
          acceptMethod(getMethodResults, isMethodResults, setMethodResults, methods[i]);
        // the bean hierarchy's methods
        for (int i = 0; i < supers.length; i++) {
          methods = supers[i].getMethods();
          for (int j = 0; j < methods.length; j++)
            acceptMethod(getMethodResults, isMethodResults, setMethodResults, methods[j]);
        }
        adaptMethodsToPropertyDescriptors(getMethodResults, isMethodResults, setMethodResults, descriptorResults);
      }
    }
    catch (JavaModelException jmex) {
      Logger.logException("Problem navigating JavaProject in BeanInfoProvider", jmex); //$NON-NLS-1$
    }

    IJavaPropertyDescriptor[] finalResults = new IJavaPropertyDescriptor[descriptorResults.size()];
    System.arraycopy(descriptorResults.toArray(), 0, finalResults, 0, descriptorResults.size());
    return finalResults;
  }

  /**
   * Retrieves the necessary information from method declaration lists, creates and fills a list of JavaPropertyDescriptors.
   * @param getMethods
   * @param isMethods
   * @param setMethods
   * @param descriptorResults
   */
  private void adaptMethodsToPropertyDescriptors(List getMethods, List isMethods, List setMethods, List descriptors) throws JavaModelException {
    List readable = new ArrayList();
    HashMap types = new HashMap();

    // iterate through get* and is* methods, updating 'readable' list and 'types' map
    filterGetMethods(getMethods, readable, types);
    filterIsMethods(isMethods, readable, types);

    // iterate set* methods, checking overlap w/ readable
    Iterator it = setMethods.iterator();
    IMethod temp = null;
    String name = ""; //$NON-NLS-1$
    String type = ""; //$NON-NLS-1$
    String[] encodedParams = null;
    String returnType = ""; //$NON-NLS-1$
    String param0 = ""; //$NON-NLS-1$

    while (it.hasNext()) {
      temp = (IMethod) it.next();
      name = createPropertyNameFromMethod(temp);
      // invalid naming convention
      if (name == null)
        continue;

      returnType = getDecodedTypeName(temp.getReturnType());
      // setter should have no return type
      if (!returnType.equals("void")) //$NON-NLS-1$
        continue;

      // need to get type from parameter
      encodedParams = temp.getParameterTypes();
      if (encodedParams != null && encodedParams.length > 0) {
        if (encodedParams.length > 1) {
          // multiple params
          param0 = getDecodedTypeName(encodedParams[0]);
          if (!param0.equals("int")) //$NON-NLS-1$
            // not a valid indexed property
            continue;
         
          type = getDecodedTypeName(encodedParams[1]);
        }
        else {
          // one param, regular setter
          if (isArray(encodedParams[0]))
            type = getDecodedTypeName(encodedParams[0]);
        }
      }

      if (readable.contains(name)) {
        // writable and readable
        if (!fRepeatMethods.contains(name)) {
          descriptors.add(new JavaPropertyDescriptor(name, (String) types.get(name), true, true));
          readable.remove(name);
          fRepeatMethods.add(name);
        }
      }
      else {
        // wasn't readable, just writable
        String[] params = temp.getParameterTypes();
        // can't be setProperty if no parameters
        if (!(params.length > 0))
          continue;
        if (!fRepeatMethods.contains(name)) {
          type = getDecodedTypeName(params[0]);
          descriptors.add(new JavaPropertyDescriptor(name, type, false, true));
          fRepeatMethods.add(name);
        }
      }
    }
    // add leftover from readable, get* and is* methods (readable = true, writable = false)
    it = readable.iterator();
    while (it.hasNext()) {
      name = (String) it.next();
      if (!fRepeatMethods.contains(name)) {
        descriptors.add(new JavaPropertyDescriptor(name, (String) types.get(name), true, false));
        fRepeatMethods.add(name);
      }
    }
  }

  private void filterGetMethods(List getMethods, List readable, HashMap types) throws JavaModelException {
    IMethod temp;
    String name;
    String encodedReturnType;
    String returnType;
    Iterator it = getMethods.iterator();
    String[] encodedParams;
    String paramType;
    // iterate get* methods
    while (it.hasNext()) {
      temp = (IMethod) it.next();
      name = createPropertyNameFromMethod(temp);
      // invalid bean naming convention
      if (name == null)
        continue;

      encodedReturnType = temp.getReturnType();
      returnType = getDecodedTypeName(encodedReturnType);

      //  can't get be a getProperty if returns void
      if (returnType.equals("void")) //$NON-NLS-1$
        continue;

      // check params in case it's indexed propety
      encodedParams = temp.getParameterTypes();
      if (encodedParams != null && encodedParams.length == 1) {
        paramType = getDecodedTypeName(encodedParams[0]);
        // syntax is > Type getter(int);
        if (!paramType.equals("int")) { //$NON-NLS-1$
          //it's not an indexed property
          continue;
        }
        // it is indexed, prop type is an ARRAY
        returnType += "[]"; //$NON-NLS-1$
      }

      readable.add(name);
      types.put(name, returnType);
    }

  }

  private void filterIsMethods(List isMethodResults, List readable, HashMap types) throws JavaModelException {
    IMethod temp;
    String name;
    String encodedReturnType;
    String returnType;
    String[] encodedParams;
    String paramType;
    // iterate is* methods
    Iterator it = isMethodResults.iterator();
    while (it.hasNext()) {
      temp = (IMethod) it.next();
      name = createPropertyNameFromMethod(temp);
      // invalid bean naming convention
      if (name == null)
        continue;
      encodedReturnType = temp.getReturnType();
      returnType = getDecodedTypeName(encodedReturnType);

      // isProperty only valid for boolean
      if (!returnType.equals("boolean")) //$NON-NLS-1$
        continue;

      // check params in case it's indexed propety
      encodedParams = temp.getParameterTypes();
      if (encodedParams != null && encodedParams.length == 1) {
        paramType = getDecodedTypeName(encodedParams[0]);
        // syntax is > Type getter(int);
        if (!paramType.equals("int")) { //$NON-NLS-1$
          //it's not a valid indexed property
          continue;
        }
      }

      readable.add(name);
      types.put(name, returnType);
    }
  }

  /**
   * Pass in a get*|set*|is* method and it will return an inferred property name using <code>Introspector.decapitalize(String)</code>
   * @param temp
   * @return an inferred property name based on the IMethod name, null if the name is not valid according to bean spec
   */
  private String createPropertyNameFromMethod(IMethod temp) {
    String name = temp.getElementName();
    if (name.startsWith("is")) //$NON-NLS-1$
      name = Introspector.decapitalize(name.substring(2));
    else
      // must be get or set
      name = Introspector.decapitalize(name.substring(3));
    return name;
  }

  /**
   * Initial filtering of methods.  Checks prefix if it's valid length.  If the prefix is "get" the  method name
   * is placed in the getMethodResults List.  If the prefix is "is", the name is added to the isMethodResults list.  If the
   * prefix is "set", it's added to the setMethodResultsList.
   *
   * @param getMethodResults
   * @param isMethodResults
   * @param setMethodResults
   * @param method
   */
  private void acceptMethod(List getMethodResults, List isMethodResults, List setMethodResults, IMethod method) throws JavaModelException {
    if (!fRepeatMethods.contains(method.getElementName())) {
      fRepeatMethods.add(method.getElementName());
      int flags = method.getFlags();
      String methodName = method.getElementName();
      if (Flags.isPublic(flags)) {
        if (methodName.length() > 3 && methodName.startsWith("get")) //$NON-NLS-1$
          getMethodResults.add(method);
        else if (methodName.length() > 2 && methodName.startsWith("is")) //$NON-NLS-1$
          isMethodResults.add(method);
        else if (methodName.length() > 3 && methodName.startsWith("set")) //$NON-NLS-1$
          setMethodResults.add(method);
      }
    }
  }

  /**
   * @param typeName
   * @return a Qualified name with the package as the qualifier, and class name as LocalName
   */
  private QualifiedName getTypeQualifiedName(String typeName) {
    StringTokenizer st = new StringTokenizer(typeName, ".", false); //$NON-NLS-1$
    int length = st.countTokens();
    int count = 0;
    StringBuffer root = new StringBuffer();
    while (count++ < length - 1) {
      root.append(st.nextToken());
      if (count < length - 1)
        root.append('.');
    }
    return new QualifiedName(root.toString(), st.nextToken());
  }

  /**
   * Checks if encodedTypeName is an array
   * @param encodedTypeName
   * @return true if encodedTypeName is an array, false otherwise.
   */
  private boolean isArray(String encodedTypeName) {
    if (encodedTypeName != null && encodedTypeName.length() > 0) {
      if (encodedTypeName.charAt(0) == '[')
        return true;
    }
    return false;
  }

  /**
   * Returns the decoded (displayable) name fo the type.
   * Either a primitive type (int, long, float...) Object (String)
   * @param type
   * @return decoded name for the encoded string
   */
  private String getDecodedTypeName(String encoded) {
    HashMap map = getEncodedTypeMap();

    StringBuffer decoded = new StringBuffer();
    char BRACKET = '[';
    String BRACKETS = "[]"; //$NON-NLS-1$
    char identifier = ' ';
    int last = 0;
    // count brackets
    while (encoded.indexOf(BRACKET, last) != -1) {
      last++;
    }
    identifier = encoded.charAt(last);
    Object primitiveType = map.get(String.valueOf(identifier));
    // L > binary type name, Q > source type name
    if (identifier == 'L' || identifier == 'Q') {
      // handle object
      String classname = encoded.substring(last + 1, encoded.length() - 1);
      decoded.append(classname);
    }
    else if (primitiveType != null) {
      // handle primitive type (from IField.getSignature())
      decoded.append((String) primitiveType);
    }
    else {
      // handle primitive type (from Class.getName())
      decoded.append(encoded);
    }
    // handle arrays
    if (last > 0) {
      for (int i = 0; i < last; i++) {
        decoded.append(BRACKETS);
      }
    }
    return decoded.toString();
  }

  /**
   *  from Class.getName() javadoc
   *  also see Signature in jdt.core api
   *<pre>
   *      B            byte
   *      C            char
   *      D            double
   *      F            float
   *      I            int
   *      J            long
   *      Lclassname;  class or interface
   *      Qsourcename; source
   *      S            short
   *      Z            boolean
   *      V          void
   *</pre>
   *
   * @return the "encoding letter" to "type" map.
   */
  private HashMap getEncodedTypeMap() {
    if (fEncodedTypeMap == null) {
      fEncodedTypeMap = new HashMap();
      fEncodedTypeMap.put("B", "byte"); //$NON-NLS-1$ //$NON-NLS-2$
      fEncodedTypeMap.put("C", "char"); //$NON-NLS-1$ //$NON-NLS-2$
      fEncodedTypeMap.put("D", "double"); //$NON-NLS-1$ //$NON-NLS-2$
      fEncodedTypeMap.put("F", "float"); //$NON-NLS-1$ //$NON-NLS-2$
      fEncodedTypeMap.put("I", "int"); //$NON-NLS-1$ //$NON-NLS-2$
      fEncodedTypeMap.put("J", "long"); //$NON-NLS-1$ //$NON-NLS-2$
      fEncodedTypeMap.put("S", "short"); //$NON-NLS-1$ //$NON-NLS-2$
      fEncodedTypeMap.put("Z", "boolean"); //$NON-NLS-1$ //$NON-NLS-2$
      fEncodedTypeMap.put("V", "void"); //$NON-NLS-1$ //$NON-NLS-2$
    }
    return fEncodedTypeMap;
  }
}
TOP

Related Classes of org.eclipse.jst.jsp.ui.internal.contentassist.BeanInfoProvider

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.