blob: 37488f3453303d2b9c9716eca4c11afdb3af23c7 [file] [log] [blame]
package org.jetbrains.plugins.javaFX.fxml.descriptors;
import com.intellij.psi.*;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.xml.*;
import com.intellij.util.ArrayUtil;
import com.intellij.xml.XmlAttributeDescriptor;
import com.intellij.xml.XmlElementDescriptor;
import com.intellij.xml.impl.BasicXmlAttributeDescriptor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.javaFX.fxml.FxmlConstants;
import org.jetbrains.plugins.javaFX.fxml.JavaFxCommonClassNames;
import org.jetbrains.plugins.javaFX.fxml.JavaFxPsiUtil;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* User: anna
* Date: 1/10/13
*/
public class JavaFxPropertyAttributeDescriptor extends BasicXmlAttributeDescriptor {
private final String myName;
private final PsiClass myPsiClass;
public JavaFxPropertyAttributeDescriptor(String name, PsiClass psiClass) {
myName = name;
myPsiClass = psiClass;
}
public PsiClass getPsiClass() {
return myPsiClass;
}
@Override
public boolean isRequired() {
return false;
}
@Override
public boolean isFixed() {
return false;
}
@Override
public boolean hasIdType() {
return false;
}
@Override
public boolean hasIdRefType() {
return false;
}
@Nullable
@Override
public String getDefaultValue() {
return null;
}
@Override
public boolean isEnumerated() {
return getEnumeratedValues() != null;
}
@Nullable
@Override
public String[] getEnumeratedValues() {
final PsiClass enumClass = getEnum();
if (enumClass != null) {
final PsiField[] fields = enumClass.getFields();
final List<String> enumConstants = new ArrayList<String>();
for (PsiField enumField : fields) {
if (isConstant(enumField)) {
enumConstants.add(enumField.getName());
}
}
return ArrayUtil.toStringArray(enumConstants);
}
final String propertyQName = getBoxedPropertyType(getDeclaration());
if (CommonClassNames.JAVA_LANG_FLOAT.equals(propertyQName) || CommonClassNames.JAVA_LANG_DOUBLE.equals(propertyQName)) {
return new String[] {"Infinity", "-Infinity", "NaN", "-NaN"};
} else if (CommonClassNames.JAVA_LANG_BOOLEAN.equals(propertyQName)) {
return new String[] {"true", "false"};
}
return null;
}
protected boolean isConstant(PsiField enumField) {
return enumField instanceof PsiEnumConstant;
}
protected PsiClass getEnum() {
final PsiClass aClass = JavaFxPsiUtil.getPropertyClass(getDeclaration());
return aClass != null && aClass.isEnum() ? aClass : null;
}
@Override
public PsiElement getEnumeratedValueDeclaration(XmlElement xmlElement, String value) {
final PsiClass aClass = getEnum();
if (aClass != null) {
final PsiField fieldByName = aClass.findFieldByName(value, false);
return fieldByName != null ? fieldByName : aClass.findFieldByName(value.toUpperCase(), false);
}
return xmlElement;
}
@Nullable
@Override
public String validateValue(XmlElement context, String value) {
if (context instanceof XmlAttributeValue) {
final XmlAttributeValue xmlAttributeValue = (XmlAttributeValue)context;
final PsiElement parent = xmlAttributeValue.getParent();
if (parent instanceof XmlAttribute) {
if (JavaFxPsiUtil.checkIfAttributeHandler((XmlAttribute)parent)) {
if (value.startsWith("#")) {
if (JavaFxPsiUtil.getControllerClass(context.getContainingFile()) == null) {
return "No controller specified for top level element";
}
}
else {
if (JavaFxPsiUtil.parseInjectedLanguages((XmlFile)context.getContainingFile()).isEmpty()) {
return "Page language not specified.";
}
}
} else if (FxmlConstants.FX_ID.equals(((XmlAttribute)parent).getName())) {
final PsiClass controllerClass = JavaFxPsiUtil.getControllerClass(context.getContainingFile());
if (controllerClass != null) {
final XmlTag xmlTag = ((XmlAttribute)parent).getParent();
if (xmlTag != null) {
final XmlElementDescriptor descriptor = xmlTag.getDescriptor();
if (descriptor instanceof JavaFxClassBackedElementDescriptor) {
final PsiElement declaration = descriptor.getDeclaration();
if (declaration instanceof PsiClass) {
final PsiField fieldByName = controllerClass.findFieldByName(xmlAttributeValue.getValue(), false);
if (fieldByName != null && !InheritanceUtil.isInheritorOrSelf((PsiClass)declaration, PsiUtil.resolveClassInType(fieldByName.getType()), true)) {
return "Cannot set " + ((PsiClass)declaration).getQualifiedName() + " to field \'" + fieldByName.getName() + "\'";
}
}
}
}
}
}
else {
final XmlAttributeDescriptor attributeDescriptor = ((XmlAttribute)parent).getDescriptor();
if (attributeDescriptor != null) {
final PsiElement declaration = attributeDescriptor.getDeclaration();
final String boxedQName = getBoxedPropertyType(declaration);
if (boxedQName != null) {
try {
final Class<?> aClass = Class.forName(boxedQName);
final Method method = aClass.getMethod(JavaFxCommonClassNames.VALUE_OF, String.class);
method.invoke(aClass, ((XmlAttributeValue)context).getValue());
}
catch (InvocationTargetException e) {
final Throwable cause = e.getCause();
if (cause instanceof NumberFormatException) {
return "Invalid value: unable to coerce to " + boxedQName;
}
}
catch (Exception ignore) {
}
}
}
}
}
}
return null;
}
@Nullable
private static String getBoxedPropertyType(PsiElement declaration) {
PsiType attrType = null;
if (declaration instanceof PsiField) {
attrType = JavaFxPsiUtil.getWrappedPropertyType((PsiField)declaration, declaration.getProject(), JavaFxCommonClassNames.ourWritableMap);
} else if (declaration instanceof PsiMethod) {
final PsiParameter[] parameters = ((PsiMethod)declaration).getParameterList().getParameters();
final boolean isStatic = ((PsiMethod)declaration).hasModifierProperty(PsiModifier.STATIC);
if (isStatic && parameters.length == 2 || !isStatic && parameters.length == 1) {
attrType = parameters[parameters.length - 1].getType();
}
}
String boxedQName = null;
if (attrType instanceof PsiPrimitiveType) {
boxedQName = ((PsiPrimitiveType)attrType).getBoxedTypeName();
} else if (PsiPrimitiveType.getUnboxedType(attrType) != null) {
final PsiClass attrClass = PsiUtil.resolveClassInType(attrType);
boxedQName = attrClass != null ? attrClass.getQualifiedName() : null;
}
return boxedQName;
}
@Override
public PsiElement getDeclaration() {
if (myPsiClass != null) {
final PsiField field = myPsiClass.findFieldByName(myName, true);
if (field != null) {
return field;
}
return JavaFxPsiUtil.findPropertySetter(myName, myPsiClass);
}
return null;
}
@Override
public PsiReference[] getValueReferences(XmlElement element, @NotNull String text) {
return !text.startsWith("${") ? super.getValueReferences(element, text) : PsiReference.EMPTY_ARRAY;
}
@Override
public String getName(PsiElement context) {
return getName();
}
@Override
public String getName() {
return myName;
}
@Override
public void init(PsiElement element) {
}
@Override
public Object[] getDependences() {
return ArrayUtil.EMPTY_OBJECT_ARRAY;
}
}