blob: 7807cd31c29f90f13a24305fd7f19c33371f0a97 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.click.eclipse.ui.editor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.apache.click.eclipse.ClickUtils;
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.JavaModelException;
import org.eclipse.jdt.core.Signature;
/**
* Provides accessors which are easy to use for JDT class model.
* This class would be used by the code completion in the Velocity editor.
*
* @author Naoki Takezoe
* @see TemplateContentAssistProcessor
*/
public class TemplateObject {
private IType type;
private String primitiveType;
/**
* The constructor.
*
* @param type the <code>IType</code> object
*/
public TemplateObject(IType type){
this.type = type;
}
/**
* The constructor for primitive types.
*
* @param primitiveType the primitive type name
*/
public TemplateObject(String primitiveType){
this.primitiveType = primitiveType;
}
/**
* Returns the specified method.
* If this object doesn't have the specified method, returns <code>null</code>.
*
* @param name the method name
* @return the specified method or <code>null</code>
*/
public TemplateObjectMethod getMethod(String name){
if(this.type!=null){
TemplateObjectMethod[] methods = getMethods();
for(int i=0;i<methods.length;i++){
if(methods[i].getName().equals(name)){
return methods[i];
}
}
}
return null;
}
/**
* Returns the specified property.
* If this object doesn't have the specified property, returns <code>null</code>.
*
* @param name the property name
* @return the specified property or <code>null</code>
*/
public TemplateObjectProperty getProperty(String name){
if(this.type!=null){
TemplateObjectProperty[] properties = getProperties();
for(int i=0;i<properties.length;i++){
if(properties[i].getName().equals(name)){
return properties[i];
}
}
}
return null;
}
/**
* Returns available methods in this object.
*
* @return the array of methods
*/
public TemplateObjectMethod[] getMethods(){
if(this.type!=null){
try {
IMethod[] methods = getAllMethods(type);
List<TemplateObjectMethod> result = new ArrayList<TemplateObjectMethod>();
for(int i=0;i<methods.length;i++){
if(Flags.isPublic(methods[i].getFlags()) && !methods[i].isConstructor()){
result.add(new TemplateObjectMethod(methods[i]));
}
}
return result.toArray(new TemplateObjectMethod[result.size()]);
} catch (JavaModelException e) {
}
}
return new TemplateObjectMethod[0];
}
/**
* Returns available fields in this object.
*
* @return the array of properties
*/
public TemplateObjectProperty[] getProperties(){
if(this.type!=null){
try {
IMethod[] methods = getAllMethods(type);
List<TemplateObjectProperty> result = new ArrayList<TemplateObjectProperty>();
for(int i=0;i<methods.length;i++){
if(Flags.isPublic(methods[i].getFlags()) && methods[i].getParameterTypes().length==0){
String name = methods[i].getElementName();
if((name.startsWith("get") && name.length() > 3) ||
(name.startsWith("is") && name.length() > 2)){
result.add(new TemplateObjectProperty(methods[i]));
}
}
}
return result.toArray(new TemplateObjectProperty[result.size()]);
} catch (JavaModelException e) {
}
}
return new TemplateObjectProperty[0];
}
/**
* Returns available methods and fields in this object.
*
* @return the array of both methods and properties
*/
public TemplateObjectElement[] getChildren(){
List<TemplateObjectElement> result = new ArrayList<TemplateObjectElement>();
if(this.type!=null){
TemplateObjectMethod[] methods = getMethods();
TemplateObjectProperty[] properties = getProperties();
for(int i=0;i<methods.length;i++){
result.add(methods[i]);
}
for(int i=0;i<properties.length;i++){
result.add(properties[i]);
}
Collections.sort(result, new Comparator<TemplateObjectElement>(){
public int compare(TemplateObjectElement arg0, TemplateObjectElement arg1) {
return arg0.toString().compareTo(arg1.toString());
}
});
}
return (TemplateObjectElement[])result.toArray(new TemplateObjectElement[result.size()]);
}
/**
* Tests whether this object is the primitive type.
*
* @return <code>true</code> if this is the primitive type;
* <code>false</code> otherwise
*/
public boolean isPrimitive(){
return this.type == null;
}
/**
* Returns the <code>IType</code> instance of this object.
*
* @return the <code>IType</code> instance.
* If this object is the primitive type, returns null.
*/
public IType getType(){
return this.type;
}
/**
* Returns the type name of this object.
*
* @return the full qualified class name or the primitive type name
*/
public String getTypeName(){
if(this.type!=null){
return this.type.getFullyQualifiedName();
} else {
return this.primitiveType;
}
}
///////////////////////////////////////////////////////////////////////////////////////
//
// Inner Classes
//
///////////////////////////////////////////////////////////////////////////////////////
/**
* The <code>TemplateObjectElement</code> implementation which expresses
* the proprty of <code>TemplateObject</code>
*/
public static class TemplateObjectProperty implements TemplateObjectElement {
private IMethod method;
public TemplateObjectProperty(IMethod method){
this.method = method;
}
public String getName(){
String name = this.method.getElementName();
if(name.startsWith("get")){
name = name.substring(3);
} else if(name.startsWith("is")){
name = name.substring(2);
}
return name.substring(0, 1).toLowerCase() + name.substring(1);
}
public String getDisplayName(){
StringBuffer sb = new StringBuffer();
sb.append(getName());
sb.append(" ");
try {
sb.append(getSimpleName(Signature.toString(this.method.getReturnType())));
} catch(Exception ex){
}
sb.append(" - ");
sb.append(this.method.getDeclaringType().getElementName());
return sb.toString();
}
/**
* Returns the <code>TemplateObject</code> by the property type.
*/
public TemplateObject toTemplateObject(){
try {
String className = ClickUtils.removeTypeParameter(Signature.toString(this.method.getReturnType()));
if(ClickUtils.isPrimitive(className)){
return null;
}
className = ClickUtils.resolveClassName(method.getDeclaringType(), className);
IJavaProject javaProject = method.getDeclaringType().getJavaProject();
IType type = javaProject.findType(className);
if(type!=null && type.exists()){
return new TemplateObject(type);
}
} catch(Exception ex){
}
return null;
}
public String toString(){
return getDisplayName();
}
}
/**
* The <code>TemplateObjectElement</code> implementation which expresses
* the method of <code>TemplateObject</code>
*/
public static class TemplateObjectMethod implements TemplateObjectElement {
private IMethod method;
public TemplateObjectMethod(IMethod method){
this.method = method;
}
public String getName(){
return this.method.getElementName();
}
// public int getArgumentCount(){
// return this.method.getParameterTypes().length;
// }
public String getDisplayName(){
StringBuffer sb = new StringBuffer();
sb.append(getName());
sb.append("(");
String[] types = this.method.getParameterTypes();
String[] names = null;
try {
names = this.method.getParameterNames();
} catch(Exception ex){
names = new String[types.length];
for(int i=0;i<names.length;i++){
names[i] = "arg" + i;
}
}
for(int i=0;i<types.length;i++){
if(i != 0){
sb.append(", ");
}
sb.append(getSimpleName(Signature.toString(types[i])));
sb.append(" ");
sb.append(names[i]);
}
sb.append(") ");
try {
sb.append(getSimpleName(Signature.toString(method.getReturnType())));
} catch(Exception ex){
}
sb.append(" - ");
sb.append(method.getDeclaringType().getElementName());
return sb.toString();
}
/**
* Returns the <code>TemplateObject</code> by the return type of the method.
*/
public TemplateObject toTemplateObject(){
// TODO This implementation is same to TemplateObjectProperty.
try {
String className = ClickUtils.removeTypeParameter(Signature.toString(this.method.getReturnType()));
if(ClickUtils.isPrimitive(className)){
return null;
}
className = ClickUtils.resolveClassName(method.getDeclaringType(), className);
IJavaProject javaProject = method.getDeclaringType().getJavaProject();
IType type = javaProject.findType(className);
if(type!=null && type.exists()){
return new TemplateObject(type);
}
} catch(Exception ex){
}
return null;
}
public String toString(){
return getDisplayName();
}
}
/**
* The interface of <code>TemplateObject</code> elements,
* expresses methods and properties.
*/
public static interface TemplateObjectElement {
/**
* Returns the element name.
*
* @return the element name
*/
public String getName();
/**
* Returns the display string.
*
* @return the display string which might contain type names or parameters
*/
public String getDisplayName();
/**
* Converts this object to the <code>TemplateObject</code>.
* <p>
* For example, if the instance corresponds a method,
* this method would return the <code>TemplateObject</code> of
* the method return type.
*
* @return the <code>TemplateObject</code> instance or <code>null</code>
*/
public TemplateObject toTemplateObject();
}
///////////////////////////////////////////////////////////////////////////////////////
//
// Utility methods
//
///////////////////////////////////////////////////////////////////////////////////////
/**
* Get the simple classname from the full qualified classname.
*
* @param name the full qualified classname
* @return the simple classname
*/
private static String getSimpleName(String name){
String simpleName = ClickUtils.removeTypeParameter(name);
if(simpleName.indexOf('.')>=0){
simpleName = simpleName.substring(simpleName.lastIndexOf('.') + 1);
}
return simpleName;
}
/**
* Returns all public methods of the given type.
*
* @param type the <code>IType</code> instance
* @return all methods of the given type, not contains main method and constructor
* @throws JavaModelException
*/
private static IMethod[] getAllMethods(IType type) throws JavaModelException {
ArrayList<IMethod> list = new ArrayList<IMethod>();
IMethod[] methods = type.getMethods();
for(int i=0;i<methods.length;i++){
if(!methods[i].isConstructor() && !methods[i].isMainMethod() && Flags.isPublic(methods[i].getFlags())){
list.add(methods[i]);
}
}
// search super class
ITypeHierarchy hierarchy = type.newSupertypeHierarchy(null);
extractMethods(hierarchy.getAllSuperclasses(type), list);
extractMethods(hierarchy.getSuperInterfaces(type), list);
if(type.isInterface()){
extractMethods(new IType[]{
type.getJavaProject().findType("java.lang.Object")}, list);
}
return (IMethod[])list.toArray(new IMethod[list.size()]);
}
private static void extractMethods(IType[] types, List<IMethod> methods) throws JavaModelException {
for(int i=0;i<types.length;i++){
IMethod[] superMethods = types[i].getMethods();
for(int j=0;j<superMethods.length;j++){
boolean flag = true;
if(!superMethods[j].isConstructor() && !superMethods[j].isMainMethod() && Flags.isPublic(superMethods[j].getFlags())){
for(int k=0;k<methods.size();k++){
IMethod method = (IMethod)methods.get(k);
if(equalsMethods(method, superMethods[j])){
flag = false;
break;
}
}
if(flag){
methods.add(superMethods[j]);
}
}
}
}
}
/**
* Tests whether given methods are same.
*
* @param method1 the <code>IMethod</code>
* @param method2 the <code>IMethod</code>
* @return <code>true</code> if given methods are same: <code>false</code> otherwise
*/
private static boolean equalsMethods(IMethod method1, IMethod method2){
if(method1.getElementName().equals(method2.getElementName())){
String[] params1 = method1.getParameterTypes();
String[] params2 = method2.getParameterTypes();
if(params1.length==params2.length){
for(int i=0;i<params1.length;i++){
if(!params1[i].equals(params2[i])){
return false;
}
}
return true;
}
}
return false;
}
}