/*******************************************************************************
* Copyright 2011 Google Inc. All Rights Reserved.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* 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 com.google.gdt.eclipse.designer.gxt.databinding.model.beans;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.gdt.eclipse.designer.gxt.databinding.ui.providers.TypeImageProvider;
import org.eclipse.wb.internal.core.databinding.model.IObserveInfo;
import org.eclipse.wb.internal.core.databinding.model.IObservePresentation;
import org.eclipse.wb.internal.core.databinding.model.presentation.SimpleObservePresentation;
import org.eclipse.wb.internal.core.databinding.model.reference.IReferenceProvider;
import org.eclipse.wb.internal.core.databinding.model.reference.StringReferenceProvider;
import org.eclipse.wb.internal.core.databinding.ui.decorate.IObserveDecorator;
import org.eclipse.wb.internal.core.databinding.utils.CoreUtils;
import org.eclipse.wb.internal.core.utils.ast.AstEditor;
import org.eclipse.wb.internal.core.utils.ast.AstNodeUtils;
import org.eclipse.wb.internal.core.utils.ast.DomGenerics;
import org.eclipse.wb.internal.core.utils.reflect.ReflectionUtils;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.apache.commons.lang.StringUtils;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*
* @author lobas_av
*
*/
public final class BeanSupport {
private final ClassLoader m_classLoader;
private final AstEditor m_editor;
private final IJavaProject m_javaProject;
private final Class<?> m_ModelDataClass;
private final Map<Class<?>, List<String>> m_properties = Maps.newHashMap();
private final Map<Class<?>, Map<String, Class<?>>> m_classes = Maps.newHashMap();
////////////////////////////////////////////////////////////////////////////
//
// Constructor
//
////////////////////////////////////////////////////////////////////////////
public BeanSupport(ClassLoader classLoader, AstEditor editor, IJavaProject javaProject)
throws Exception {
m_classLoader = classLoader;
m_editor = editor;
m_javaProject = javaProject == null ? editor.getJavaProject() : javaProject;
m_ModelDataClass = classLoader.loadClass("com.extjs.gxt.ui.client.data.ModelData");
}
////////////////////////////////////////////////////////////////////////////
//
// Access
//
////////////////////////////////////////////////////////////////////////////
public List<BeanPropertyObserveInfo> getProperties(Class<?> beanClass,
IObserveInfo parent,
boolean addSelf) throws Exception {
List<BeanPropertyObserveInfo> properties = Lists.newArrayList();
//
if (addSelf) {
SimpleObservePresentation selfPresentation =
new SimpleObservePresentation("<Self Object>",
"",
TypeImageProvider.SELF_OBJECT_PROPERTY_IMAGE);
properties.add(new BeanPropertyObserveInfo(null,
parent,
StringReferenceProvider.EMPTY,
selfPresentation,
IObserveDecorator.BOLD));
}
//
for (String propertyName : getStringProperties(beanClass)) {
Class<?> propertyClass = m_classes.get(beanClass).get(propertyName);
if (propertyClass == null) {
propertyClass = Object.class;
}
IReferenceProvider referenceProvider =
new StringReferenceProvider("\"" + propertyName + "\"");
IObservePresentation presentation =
new SimpleObservePresentation(propertyName,
propertyName,
TypeImageProvider.getImage(propertyClass));
IObserveDecorator decorator = IObserveDecorator.DEFAULT;
properties.add(new BeanPropertyObserveInfo(propertyClass,
parent,
referenceProvider,
presentation,
decorator));
}
return properties;
}
private List<String> getStringProperties(Class<?> beanClass) throws Exception {
List<String> properties = m_properties.get(beanClass);
if (properties != null) {
return properties;
}
if (m_ModelDataClass.isAssignableFrom(beanClass)) {
IType type = m_javaProject.findType(beanClass.getName());
if (type != null) {
ICompilationUnit compilationUnit = type.getCompilationUnit();
if (compilationUnit != null) {
properties = Lists.newArrayList();
m_properties.put(beanClass, properties);
//
Map<String, Class<?>> classes = Maps.newHashMap();
m_classes.put(beanClass, classes);
//
Set<String> uniqueProperties = Sets.newHashSet();
AstEditor editor = new AstEditor(compilationUnit);
editor.getAstUnit().accept(new PropertiesVisitor(uniqueProperties, classes));
//
Class<?> superClass = beanClass.getSuperclass();
if (superClass != null) {
uniqueProperties.addAll(getStringProperties(superClass));
//
Map<String, Class<?>> superClasses = m_classes.get(superClass);
if (superClasses != null) {
for (Map.Entry<String, Class<?>> entry : superClasses.entrySet()) {
if (!classes.containsKey(entry.getKey())) {
classes.put(entry.getKey(), entry.getValue());
}
}
}
}
//
properties.addAll(uniqueProperties);
Collections.sort(properties);
//
return properties;
}
}
}
return Collections.emptyList();
}
private class PropertiesVisitor extends ASTVisitor {
private final Set<String> m_properties;
private final Map<String, Class<?>> m_classes;
////////////////////////////////////////////////////////////////////////////
//
// Constructor
//
////////////////////////////////////////////////////////////////////////////
public PropertiesVisitor(Set<String> properties, Map<String, Class<?>> classes) {
m_properties = properties;
m_classes = classes;
}
////////////////////////////////////////////////////////////////////////////
//
// ASTVisitor
//
////////////////////////////////////////////////////////////////////////////
@Override
public void endVisit(MethodInvocation invocation) {
try {
String signature = AstNodeUtils.getMethodSignature(invocation);
List<Expression> arguments = DomGenerics.arguments(invocation);
if (signature.equals("get(java.lang.String)")
|| signature.startsWith("set(java.lang.String,")
&& arguments.size() == 2) {
String className;
if (arguments.size() == 1) {
className =
AstNodeUtils.getFullyQualifiedName(
AstNodeUtils.getMethodBinding(invocation).getReturnType(),
true);
} else {
className = AstNodeUtils.getFullyQualifiedName(arguments.get(1), true);
}
String propertyName = null;
if (m_editor == null) {
Expression expression = arguments.get(0);
if (expression instanceof StringLiteral) {
StringLiteral literal = (StringLiteral) expression;
propertyName = literal.getLiteralValue();
}
} else {
propertyName = CoreUtils.evaluate(String.class, m_editor, arguments.get(0));
}
if (!StringUtils.isEmpty(propertyName)) {
m_properties.add(propertyName);
if (!className.equals("java.lang.Object") && !m_classes.containsKey(propertyName)) {
try {
m_classes.put(
propertyName,
ReflectionUtils.getClassByName(m_classLoader, className));
} catch (Throwable e) {
System.out.println(className + " = " + e); // XXX
}
}
}
}
} catch (Throwable e) {
System.out.println(e); // XXX
}
}
}
}