/* | |
* 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; | |
} | |
} |