/*
* Copyright 2003-2015 JetBrains s.r.o.
*
* Licensed 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 jetbrains.mps.idea.testFramework;
import com.intellij.util.xmlb.annotations.Transient;
import jetbrains.mps.idea.testFramework.MpsBeanAdjuster.IllegalBeanFormatException;
import org.jetbrains.annotations.NotNull;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class BeanCompatibilityChecker {
private final Class<? extends MpsBean> myClass;
public BeanCompatibilityChecker(Class<? extends MpsBean> aClass) {
myClass = aClass;
}
@NotNull
public List<Field> checkAndReturnFields() {
List<Field> filteredFields = collectNonTransientFields();
check(filteredFields);
return filteredFields;
}
private void check(List<Field> fields) {
for (Field field : fields) {
int modifiersMask = field.getModifiers();
if (Modifier.isFinal(modifiersMask) || Modifier.isStatic(modifiersMask)) {
throw new IllegalBeanFormatException("Illegal modifiers at the field '" + field.getName() + "'.");
}
if (!Modifier.isPublic(modifiersMask)) {
throw new IllegalBeanFormatException("Illegal modifiers at the field '" + field.getName() + "'. Only `public` is allowed");
}
if (field.getType().isPrimitive()) {
throw new IllegalBeanFormatException("The primitive field '" + field.getName() + "'. Primitive fields are not allowed.");
}
Type type = field.getGenericType();
Set<Class<?>> allowedTypes = Entry.getAllowedTypes();
if (!allowedTypes.contains(field.getType()) || !checkGenericCase(type)) {
throw new IllegalBeanFormatException("Only these types of field are allowed " + allowedTypes + ". " +
" The field '" + field.getName() + "' has a type " + type);
}
}
}
private List<Field> collectNonTransientFields() {
List<Field> result = new ArrayList<Field>();
for (Field field : myClass.getFields()) {
int modifiersMask = field.getModifiers();
if (!isTransient(field, modifiersMask)) {
result.add(field);
}
}
return result;
}
/**
* only List<String> is an acceptable generic
*/
private boolean checkGenericCase(Type type) {
if (type instanceof ParameterizedType) {
boolean isList = ((ParameterizedType) type).getRawType().equals(List.class);
Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
boolean isGenericOfString = actualTypeArguments.length == 1 && actualTypeArguments[0].equals(String.class);
return isList && isGenericOfString;
}
return true;
}
private boolean isTransient(Field field, int modifiersMask) {
boolean skipField = false;
for (Annotation annotation : field.getAnnotations()) {
if (annotation.getClass() == Transient.class || Modifier.isTransient(modifiersMask)) {
skipField = true;
break;
}
}
return skipField;
}
}