/* * DBeaver - Universal Database Manager * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) * * 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 org.jkiss.dbeaver.runtime.properties; import org.jkiss.code.NotNull; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.DBConstants; import org.jkiss.dbeaver.model.preferences.DBPPropertySource; import org.jkiss.dbeaver.model.meta.IPropertyCacheValidator; import org.jkiss.dbeaver.model.meta.LazyProperty; import org.jkiss.dbeaver.model.meta.Property; import org.jkiss.dbeaver.model.meta.PropertyGroup; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.utils.BeanUtils; import org.jkiss.utils.CommonUtils; import java.lang.reflect.Method; import java.util.*; /** * Abstract object attribute */ public abstract class ObjectAttributeDescriptor { static final Log log = Log.getLog(ObjectAttributeDescriptor.class); public static final Comparator<ObjectAttributeDescriptor> ATTRIBUTE_DESCRIPTOR_COMPARATOR = new Comparator<ObjectAttributeDescriptor>() { @Override public int compare(ObjectAttributeDescriptor o1, ObjectAttributeDescriptor o2) { return o1.getOrderNumber() - o2.getOrderNumber(); } }; private final DBPPropertySource source; private ObjectPropertyGroupDescriptor parent; private int orderNumber; private String id; private Method getter; private boolean isLazy; private IPropertyCacheValidator cacheValidator; private Class<?> declaringClass; public ObjectAttributeDescriptor( DBPPropertySource source, ObjectPropertyGroupDescriptor parent, Method getter, String id, int orderNumber) { this.source = source; this.parent = parent; this.getter = getter; this.orderNumber = orderNumber; this.id = id; if (CommonUtils.isEmpty(this.id)) { this.id = BeanUtils.getPropertyNameFromGetter(getter.getName()); } declaringClass = parent == null ? getter.getDeclaringClass() : parent.getDeclaringClass(); if (this.getter.getParameterTypes().length == 1 && getter.getParameterTypes()[0] == DBRProgressMonitor.class) { this.isLazy = true; } if (isLazy) { final LazyProperty lazyInfo = getter.getAnnotation(LazyProperty.class); if (lazyInfo != null) { try { cacheValidator = lazyInfo.cacheValidator().newInstance(); } catch (Exception e) { log.warn("Can't instantiate lazy cache validator '" + lazyInfo.cacheValidator().getName() + "'", e); } } } } public Class<?> getDeclaringClass() { return declaringClass; } public DBPPropertySource getSource() { return source; } public int getOrderNumber() { return orderNumber; } @NotNull public String getId() { return id; } public Method getGetter() { return getter; } public boolean isNameProperty() { return id.equals(DBConstants.PROP_ID_NAME); } public boolean isRemote() { return isLazy || parent != null && parent.isRemote(); } public boolean isLazy() { return isLazy; } public boolean isLazy(Object object, boolean checkParent) { if (isLazy && cacheValidator != null) { if (parent != null) { if (parent.isLazy(object, true)) { return true; } try { // Parent isn't lazy so use null progress monitor object = parent.getGroupObject(object, null); } catch (Exception e) { log.debug(e); return true; } } return !cacheValidator.isPropertyCached(object, id); } return isLazy || (checkParent && parent != null && parent.isLazy(object, checkParent)); } public IPropertyCacheValidator getCacheValidator() { return cacheValidator; } public ObjectPropertyGroupDescriptor getParent() { return parent; } public abstract String getCategory(); public abstract String getDescription(); public static List<ObjectPropertyDescriptor> extractAnnotations( DBPPropertySource source, Class<?> theClass, IPropertyFilter filter) { List<ObjectPropertyDescriptor> annoProps = new ArrayList<ObjectPropertyDescriptor>(); extractAnnotations(source, null, theClass, annoProps, filter); return annoProps; } public static List<ObjectPropertyDescriptor> extractAnnotations( DBPPropertySource source, Collection<Class<?>> classList, IPropertyFilter filter) { List<ObjectPropertyDescriptor> annoProps = new ArrayList<>(); for (Class<?> objectClass : classList) { annoProps.addAll(ObjectAttributeDescriptor.extractAnnotations(source, objectClass, filter)); } Collections.sort(annoProps, ATTRIBUTE_DESCRIPTOR_COMPARATOR); return annoProps; } static void extractAnnotations(DBPPropertySource source, ObjectPropertyGroupDescriptor parent, Class<?> theClass, List<ObjectPropertyDescriptor> annoProps, IPropertyFilter filter) { Method[] methods = theClass.getMethods(); Map<String, Method> passedNames = new HashMap<>(); for (Method method : methods) { String methodFullName = method.getDeclaringClass().getName() + "." + method.getName(); final Method prevMethod = passedNames.get(methodFullName); if (prevMethod != null) { // The same method but probably with another return type final Class<?> prevReturnType = prevMethod.getReturnType(); final Class<?> newReturnType = method.getReturnType(); if (newReturnType == null || prevReturnType == null || newReturnType == prevReturnType || !prevReturnType.isAssignableFrom(newReturnType)) { continue; } // Let it another chance. New return types seems to be subclass of previous } final PropertyGroup propGroupInfo = method.getAnnotation(PropertyGroup.class); if (propGroupInfo != null && method.getReturnType() != null) { // Property group ObjectPropertyGroupDescriptor groupDescriptor = new ObjectPropertyGroupDescriptor(source, parent, method, propGroupInfo, filter); annoProps.addAll(groupDescriptor.getChildren()); } else { final Property propInfo = method.getAnnotation(Property.class); if (propInfo == null || !BeanUtils.isGetterName(method.getName()) || method.getReturnType() == null) { continue; } // Single property ObjectPropertyDescriptor desc = new ObjectPropertyDescriptor(source, parent, propInfo, method); if (filter != null && !filter.select(desc)) { continue; } if (prevMethod != null) { // Remove previous anno for (Iterator<ObjectPropertyDescriptor> iter = annoProps.iterator(); iter.hasNext(); ) { if (iter.next().getId().equals(desc.getId())) { iter.remove(); } } } annoProps.add(desc); passedNames.put(methodFullName, method); } } Collections.sort(annoProps, ATTRIBUTE_DESCRIPTOR_COMPARATOR); } }